@@ -98,8 +98,9 @@ func (p *expoHistogramDataPoint[N]) record(v N) {
9898 // If the new bin would make the counts larger than maxScale, we need to
9999 // downscale current measurements.
100100 scaleDelta := p .scaleChange (bin , bucket .startBin , len (bucket .counts ))
101- if scaleDelta <= 0 {
102- bucket .record (bin )
101+ if scaleDelta <= 0 && ! bucket .needsResize (bin ) {
102+ // Fast path without requiring the full lock
103+ bucket .recordWithoutResize (bin )
103104 p .scaleMux .RUnlock ()
104105 return
105106 }
@@ -110,18 +111,19 @@ func (p *expoHistogramDataPoint[N]) record(v N) {
110111 p .scaleMux .RUnlock ()
111112 return
112113 }
113- // Switch to a full Lock for downscaling
114+ // Switch to a full Lock for downscaling or resizing
114115 p .scaleMux .RUnlock ()
115116 p .scaleMux .Lock ()
116117 defer p .scaleMux .Unlock ()
117- // recompute the scaleDelta now that we hold the exclusive lock
118+ // recompute the scaleDelta now that we hold the full lock
118119 scaleDelta = p .scaleChange (bin , bucket .startBin , len (bucket .counts ))
119120 // Downscale
120- p .scale -= scaleDelta
121- p .posBuckets .downscale (scaleDelta )
122- p .negBuckets .downscale (scaleDelta )
121+ if scaleDelta > 0 {
122+ p .scale -= scaleDelta
123+ p .posBuckets .downscale (scaleDelta )
124+ p .negBuckets .downscale (scaleDelta )
125+ }
123126
124- // TODO: is it worth switching back to a read-lock?
125127 bucket .record (p .getBin (absV ))
126128}
127129
@@ -202,6 +204,15 @@ type expoBuckets struct {
202204 counts []uint64
203205}
204206
207+ func (b * expoBuckets ) needsResize (bin int32 ) bool {
208+ endBin := int (b .startBin ) + len (b .counts ) - 1
209+ return len (b .counts ) == 0 || bin < b .startBin || int (bin ) > endBin
210+ }
211+
212+ func (b * expoBuckets ) recordWithoutResize (bin int32 ) {
213+ atomic .AddUint64 (& b .counts [bin - b .startBin ], 1 )
214+ }
215+
205216// record increments the count for the given bin, and expands the buckets if needed.
206217// Size changes must be done before calling this function.
207218func (b * expoBuckets ) record (bin int32 ) {
@@ -215,6 +226,7 @@ func (b *expoBuckets) record(bin int32) {
215226
216227 // if the new bin is inside the current range
217228 if bin >= b .startBin && int (bin ) <= endBin {
229+ // No need to use atomics since we hold the RW lock.
218230 b .counts [bin - b .startBin ]++
219231 return
220232 }
0 commit comments