-
Notifications
You must be signed in to change notification settings - Fork 90
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use of LongAdder in StatisticsCollector #140
Comments
Attaching a simple benchmark that is trying to reproduce a very busy thread pool used for batching: import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAdder;
public class AtomicVsAdder {
private static final ExecutorService EXECUTOR = Executors.newCachedThreadPool();
public static void main(final String[] args) throws ExecutionException, InterruptedException {
// knobs
final Strategy strategy = new LongAdderStrategy();
final long iterations = 100L * 1000 * 1000;
// test
System.out.println("testing with #cpu=" + Runtime.getRuntime().availableProcessors());
final List<Future<?>> futures = new ArrayList<>();
for (final int nThreads : List.of(1, 2, 4, 8, 16)) {
System.out.println("start test with " + nThreads + " thread");
final long start = System.nanoTime();
for (int i = 0; i < nThreads; i++) {
Future<?> submit = EXECUTOR.submit(() -> concurrentWork(strategy, iterations));
futures.add(submit);
}
for (final Future<?> future : futures) {
future.get(); // wait for all
}
final long end = System.nanoTime();
System.out.println("done in " + Duration.ofNanos(end - start).toMillis() + "ms => result " + strategy.get());
strategy.reset();
}
System.out.println("the end");
EXECUTOR.shutdownNow();
}
@SuppressWarnings("SameParameterValue")
private static void concurrentWork(final Strategy strategy, final long iterations) {
long work = iterations;
while (work-- > 0) {
strategy.increment();
}
}
interface Strategy {
void increment();
long get();
void reset();
}
static class LongAdderStrategy implements Strategy {
private LongAdder longAdder = new LongAdder();
@Override
public void increment() {
longAdder.increment();
}
@Override
public long get() {
return longAdder.sum();
}
@Override
public void reset() {
longAdder = new LongAdder();
}
}
static class AtomicLongStrategy implements Strategy {
private final AtomicLong atomicLong = new AtomicLong(0);
@Override
public void increment() {
atomicLong.incrementAndGet();
}
@Override
public long get() {
return atomicLong.get();
}
@Override
public void reset() {
atomicLong.set(0);
}
}
} On my workstation I get the following numbers for
and the following for
Basically @bbakerman @dondonz what do you think? |
By all means add a new version that breaks the contract and does not return the value. By the way NoOpStatisticsCollector is used by default right - so people have to opt into any of this anyway |
According to javadoc:
I tried to update the
StatisticsCollector
to useLongAdder
but it is not really possible to make it very efficient,because
StatisticsCollector
uses increment and get pattern.Example:
With
LongAdder
it would be something like:sum()
is triggered on every call, so it could slow down a bit, but luckily the return value is never used, at least internally.I'm aware that changing all methods to return void it is a breaking change but in theory it could greatly improve performance under high contention (i.e. lot of threads updating statistics).
In case you're interested, here is the implementation:
The text was updated successfully, but these errors were encountered: