Skip to content

Commit 961f8ca

Browse files
committed
Hopefully final optimization.
benchstat -delta-test=none v6.txt v9.txt name old time/op new time/op delta CachedTGatherer_Update/Update_of_one_element_without_reset-12 13.1ms ± 0% 0.0ms ± 0% -99.81% CachedTGatherer_Update/Update_of_all_elements_with_reset-12 309ms ± 0% 282ms ± 0% -8.77% CachedTGatherer_Update/Gather-12 422ms ± 0% 0ms ± 0% -99.95% name old alloc/op new alloc/op delta CachedTGatherer_Update/Update_of_one_element_without_reset-12 208B ± 0% 208B ± 0% 0.00% CachedTGatherer_Update/Update_of_all_elements_with_reset-12 2.47kB ± 0% 1.67kB ± 0% -32.56% CachedTGatherer_Update/Gather-12 52.8kB ± 0% 24.6kB ± 0% -53.34% name old allocs/op new allocs/op delta CachedTGatherer_Update/Update_of_one_element_without_reset-12 3.00 ± 0% 3.00 ± 0% 0.00% CachedTGatherer_Update/Update_of_all_elements_with_reset-12 0.00 0.00 0.00% CachedTGatherer_Update/Gather-12 1.00k ± 0% 0.00k ± 0% -99.60% Signed-off-by: Bartlomiej Plotka <[email protected]>
1 parent 11bdfa3 commit 961f8ca

File tree

1 file changed

+29
-25
lines changed

1 file changed

+29
-25
lines changed

prometheus/cache/cache.go

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,13 @@ var separatorByteSlice = []byte{model.SeparatorByte} // For convenient use with
4545
type CachedTGatherer struct {
4646
metricFamiliesByName map[string]*family
4747
mMu sync.RWMutex
48+
49+
desiredTouchState bool
4850
}
4951

5052
func NewCachedTGatherer() *CachedTGatherer {
5153
return &CachedTGatherer{
54+
desiredTouchState: true,
5255
metricFamiliesByName: map[string]*family{},
5356
}
5457
}
@@ -57,30 +60,19 @@ type family struct {
5760
*dto.MetricFamily
5861

5962
metricsByHash map[uint64]*metric
60-
touched bool
63+
touchState bool
64+
needsRebuild bool
6165
}
6266

6367
type metric struct {
6468
*dto.Metric
65-
touched bool
69+
touchState bool
6670
}
6771

6872
// normalizeMetricFamilies returns a MetricFamily slice with empty
6973
// MetricFamilies pruned and the remaining MetricFamilies sorted by name within
7074
// the slice, with the contained Metrics sorted within each MetricFamily.
7175
func normalizeMetricFamilies(metricFamiliesByName map[string]*family) []*dto.MetricFamily {
72-
// TODO(bwplotka): We could optimize this further by bookkeeping this slice in place.
73-
for _, mf := range metricFamiliesByName {
74-
if cap(mf.Metric) < len(mf.metricsByHash) {
75-
mf.Metric = make([]*dto.Metric, 0, len(mf.metricsByHash))
76-
}
77-
mf.Metric = mf.Metric[:0]
78-
for _, m := range mf.metricsByHash {
79-
mf.Metric = append(mf.Metric, m.Metric)
80-
}
81-
sort.Sort(internal.MetricSorter(mf.Metric))
82-
}
83-
8476
names := make([]string, 0, len(metricFamiliesByName))
8577
for name, mf := range metricFamiliesByName {
8678
if len(mf.Metric) > 0 {
@@ -98,9 +90,6 @@ func normalizeMetricFamilies(metricFamiliesByName map[string]*family) []*dto.Met
9890
// Gather implements TransactionalGatherer interface.
9991
func (c *CachedTGatherer) Gather() (_ []*dto.MetricFamily, done func(), err error) {
10092
c.mMu.RLock()
101-
102-
// BenchmarkCachedTGatherer_Update shows, even for 1 million metrics among 1000 families
103-
// this is efficient enough (~400ms and ~50 kB per op), no need to cache it for now.
10493
return normalizeMetricFamilies(c.metricFamiliesByName), c.mMu.RUnlock, nil
10594
}
10695

@@ -180,7 +169,10 @@ func (c *CachedTGatherer) Update(reset bool, inserts []Insert, deletions []Key)
180169
}
181170
mf.Name = &inserts[i].FQName
182171
}
183-
mf.touched = true
172+
if reset {
173+
mf.touchState = c.desiredTouchState
174+
}
175+
184176
mf.Type = inserts[i].ValueType.ToDTO()
185177
mf.Help = &inserts[i].Help
186178

@@ -200,8 +192,11 @@ func (c *CachedTGatherer) Update(reset bool, inserts []Insert, deletions []Key)
200192
})
201193
}
202194
sort.Sort(internal.LabelPairSorter(m.Label))
195+
mf.needsRebuild = true
196+
}
197+
if reset {
198+
m.touchState = c.desiredTouchState
203199
}
204-
m.touched = true
205200

206201
switch inserts[i].ValueType {
207202
case prometheus.CounterValue:
@@ -263,18 +258,19 @@ func (c *CachedTGatherer) Update(reset bool, inserts []Insert, deletions []Key)
263258
continue
264259
}
265260

261+
mf.needsRebuild = true
266262
delete(mf.metricsByHash, hSum)
267263
}
268264

269265
if reset {
270266
// Trading off-time instead of memory allocated for otherwise needed replacement map.
271267
for name, mf := range c.metricFamiliesByName {
272-
if !mf.touched {
268+
if mf.touchState != c.desiredTouchState {
273269
delete(c.metricFamiliesByName, name)
274270
continue
275271
}
276272
for hash, m := range mf.metricsByHash {
277-
if !m.touched {
273+
if m.touchState != c.desiredTouchState {
278274
delete(mf.metricsByHash, hash)
279275
continue
280276
}
@@ -283,15 +279,23 @@ func (c *CachedTGatherer) Update(reset bool, inserts []Insert, deletions []Key)
283279
delete(c.metricFamiliesByName, name)
284280
}
285281
}
282+
283+
// Avoid resetting state by flipping what we will expect in the next update.
284+
c.desiredTouchState = !c.desiredTouchState
286285
}
287286

288-
// TODO(bwplotka): Potentially move this only for reset, but then code would assume
289-
// you either only update or only reset update. For now we can live with small overhead.
290287
for _, mf := range c.metricFamiliesByName {
291-
mf.touched = false
288+
if !mf.needsRebuild {
289+
continue
290+
}
291+
292+
mf.Metric = mf.Metric[:0]
292293
for _, m := range mf.metricsByHash {
293-
m.touched = false
294+
mf.Metric = append(mf.Metric, m.Metric)
294295
}
296+
sort.Sort(internal.MetricSorter(mf.Metric))
297+
298+
mf.needsRebuild = false
295299
}
296300

297301
return errs.MaybeUnwrap()

0 commit comments

Comments
 (0)