patternjavaMinor
Concurrent event counter/averager utility classes
Viewed 0 times
counterutilityclassesconcurrentaveragerevent
Problem
For a personal project, I'm working on a Java profiler that specifically targets ThreadPoolExecutor and its subclasses and provides statistics about throughput of executed tasks. To support this I've written the following utility classes, which are intended to help compute a smoothed average rate of events over time. They are intended to be thread-safe and lock-free; I want to interfere as little as possible with the threads that are counting (and executing) tasks.
First is the EventMeter, which is used to count one type of event. This supports computing an average rate of events by keeping a running sum of counted events and being able to discard old data. (Pardon my TODO.)
```
public final class EventMeter {
private final long bucketLengthNanos;
private final double[] buckets;
private volatile double sum;
private int bucketNum;
private AtomicLong accumulator;
public EventMeter(int numBuckets, long bucketLengthNanos) {
// TODO assert that numBuckets >= 1, bucketLengthNanos >= some small number
this.bucketLengthNanos = bucketLengthNanos;
this.buckets = new double[numBuckets];
this.sum = 0.0;
this.bucketNum = 0;
this.accumulator = new AtomicLong(Double.doubleToLongBits(0.0));
}
public long getBucketLengthNanos() {
return bucketLengthNanos;
}
public void countEvent() {
long oldAccumulator;
long newAccumulator;
do {
oldAccumulator = accumulator.get();
newAccumulator = Double.doubleToLongBits(Double.longBitsToDouble(oldAccumulator) + 1.0);
} while (!accumulator.compareAndSet(oldAccumulator, newAccumulator));
}
public double getRate() {
return sum / buckets.length;
}
public void fillBucketFromAccumulator(double accumulatorFraction) {
long oldAccumulator;
long newAccumulator;
do {
oldAccumulator = accumulator.get();
newAccumulator = Double.doub
First is the EventMeter, which is used to count one type of event. This supports computing an average rate of events by keeping a running sum of counted events and being able to discard old data. (Pardon my TODO.)
```
public final class EventMeter {
private final long bucketLengthNanos;
private final double[] buckets;
private volatile double sum;
private int bucketNum;
private AtomicLong accumulator;
public EventMeter(int numBuckets, long bucketLengthNanos) {
// TODO assert that numBuckets >= 1, bucketLengthNanos >= some small number
this.bucketLengthNanos = bucketLengthNanos;
this.buckets = new double[numBuckets];
this.sum = 0.0;
this.bucketNum = 0;
this.accumulator = new AtomicLong(Double.doubleToLongBits(0.0));
}
public long getBucketLengthNanos() {
return bucketLengthNanos;
}
public void countEvent() {
long oldAccumulator;
long newAccumulator;
do {
oldAccumulator = accumulator.get();
newAccumulator = Double.doubleToLongBits(Double.longBitsToDouble(oldAccumulator) + 1.0);
} while (!accumulator.compareAndSet(oldAccumulator, newAccumulator));
}
public double getRate() {
return sum / buckets.length;
}
public void fillBucketFromAccumulator(double accumulatorFraction) {
long oldAccumulator;
long newAccumulator;
do {
oldAccumulator = accumulator.get();
newAccumulator = Double.doub
Solution
Method
fillBucketFromAccumulator() is not thread-safe. Two threads can write to the same slot in the array, and then both advance by one.Context
StackExchange Code Review Q#3912, answer score: 4
Revisions (0)
No revisions yet.