File: LongConcurrentHistogram.cs
protected override void IncrementCountAtIndex(int index)
{
long criticalValue = _wrp.WriterCriticalSectionEnter();
try
{
_counts.IncrementAndGet(index); // Interlocked.Add
Interlocked.Increment(ref _totalCount); // second atomic op
}
finally { _wrp.WriterCriticalSectionExit(criticalValue); }
}
Every RecordValue call in the concurrent histogram pays for WriterCriticalSectionEnter (an Interlocked.Increment), Interlocked.Add on the count, Interlocked.Increment on _totalCount, and WriterCriticalSectionExit (another Interlocked.Increment). That's four atomic operations per record.
_totalCount is only read by TotalCount which already calls Interlocked.Read. Consider whether _totalCount needs to be updated atomically at all during recording, or whether it can be derived lazily by summing the array (expensive) or maintained with a Volatile.Write (cheaper). The Java original avoids this cost by using a single updater CAS. A Recorder-pattern approach (per-thread histograms merged on read) avoids the issue entirely and is the recommended usage pattern anyway.
File:
LongConcurrentHistogram.csEvery
RecordValuecall in the concurrent histogram pays forWriterCriticalSectionEnter(anInterlocked.Increment),Interlocked.Addon the count,Interlocked.Incrementon_totalCount, andWriterCriticalSectionExit(anotherInterlocked.Increment). That's four atomic operations per record._totalCountis only read byTotalCountwhich already callsInterlocked.Read. Consider whether_totalCountneeds to be updated atomically at all during recording, or whether it can be derived lazily by summing the array (expensive) or maintained with aVolatile.Write(cheaper). The Java original avoids this cost by using a singleupdaterCAS. ARecorder-pattern approach (per-thread histograms merged on read) avoids the issue entirely and is the recommended usage pattern anyway.