1818class Buffer {
1919
2020 private static final long bufferActiveBit = 1L << 63 ;
21+ // Tracking observation counts requires an AtomicLong for coordination between recording and
22+ // collecting. AtomicLong does much worse under contention than the LongAdder instances used
23+ // elsewhere to hold aggregated state. To improve, we stripe the AtomicLong into N instances,
24+ // where N is the number of available processors. Each record operation chooses the appropriate
25+ // instance to use based on the modulo of its thread id and N. This is a more naive / simple
26+ // implementation compared to the striping used under the hood in java.util.concurrent classes
27+ // like LongAdder - contention and hot spots can still occur if recording thread ids happen to
28+ // resolve to the same index. Further improvement is possible.
2129 private final AtomicLong [] stripedObservationCounts ;
2230 private double [] observationBuffer = new double [0 ];
2331 private int bufferPos = 0 ;
@@ -35,9 +43,8 @@ class Buffer {
3543 }
3644
3745 boolean append (double value ) {
38- AtomicLong observationCountForThread =
39- stripedObservationCounts [
40- ((int ) Thread .currentThread ().getId ()) % stripedObservationCounts .length ];
46+ int index = Math .abs ((int ) Thread .currentThread ().getId ()) % stripedObservationCounts .length ;
47+ AtomicLong observationCountForThread = stripedObservationCounts [index ];
4148 long count = observationCountForThread .incrementAndGet ();
4249 if ((count & bufferActiveBit ) == 0 ) {
4350 return false ; // sign bit not set -> buffer not active.
@@ -79,7 +86,7 @@ <T extends DataPointSnapshot> T run(
7986 runLock .lock ();
8087 try {
8188 // Signal that the buffer is active.
82- Long expectedCount = 0L ;
89+ long expectedCount = 0L ;
8390 for (AtomicLong observationCount : stripedObservationCounts ) {
8491 expectedCount += observationCount .getAndAdd (bufferActiveBit );
8592 }
@@ -97,12 +104,12 @@ <T extends DataPointSnapshot> T run(
97104 long expectedBufferSize = 0 ;
98105 if (reset ) {
99106 for (AtomicLong observationCount : stripedObservationCounts ) {
100- expectedBufferSize += ( int ) ( observationCount .getAndSet (0 ) & ~bufferActiveBit ) ;
107+ expectedBufferSize += observationCount .getAndSet (0 ) & ~bufferActiveBit ;
101108 }
102109 reset = false ;
103110 } else {
104111 for (AtomicLong observationCount : stripedObservationCounts ) {
105- expectedBufferSize += ( int ) observationCount .addAndGet (bufferActiveBit );
112+ expectedBufferSize += observationCount .addAndGet (bufferActiveBit );
106113 }
107114 }
108115 expectedBufferSize -= expectedCount ;
0 commit comments