a situation when JHM 1.21 measurements are broken
Valentin Kovalenko
valentin.male.kovalenko at gmail.com
Tue Feb 5 12:20:07 UTC 2019
I encountered a simple situation where JMH 1.21 produces obviously
incorrect results. The test (is also temporarily available at
https://github.com/stIncMale/sandbox/blob/master/benchmarks/src/test/java/stincmale/sandbox/benchmarks/TmpTest.java
,
you can run it with `mvn clean test -f benchmarks/pom.xml -Dtest=TmpTest`):
public class TmpTest {
public TmpTest() {
}
public final void runThroughputBenchmarks(final int numberOfThreads)
throws RunnerException {
final OptionsBuilder opts = new OptionsBuilder();
opts.jvmArgs("-Xfuture", "--illegal-access=deny", "-Xshare:off",
"-Xms1024m", "-Xmx1024m", "-server", "-disableassertions")
.shouldDoGC(false)
.syncIterations(true)
.shouldFailOnError(true)
.threads(1)
.timeout(milliseconds(1000_000))
.forks(2)
.warmupTime(milliseconds(1000))
.warmupIterations(6)
.measurementTime(milliseconds(1000))
.measurementIterations(2)
.include(includeBenchmarks(TmpTest.class))
.shouldDoGC(false)
.mode(Mode.Throughput)
.timeUnit(TimeUnit.MILLISECONDS)
.threads(numberOfThreads);
new Runner(opts.build()).run();
}
@Test
public final void throughputThreads4() throws RunnerException {
runThroughputBenchmarks(4);
}
@Test
public final void throughputThreads32() throws RunnerException {
runThroughputBenchmarks(32);
}
@Benchmark
public final long reentrantRwLock(final BenchmarkState s) throws
InterruptedException {
s.rwLock.readLock().lock();
try {
if (s.counter.getAndIncrement() < 1000) {
return s.counter.get();
}
} finally {
s.rwLock.readLock().unlock();
}
s.rwLock.writeLock().lock();
try {
Thread.sleep(10);
s.counter.set(0);
} finally {
s.rwLock.writeLock().unlock();
}
return s.counter.get();
}
@Benchmark
public final long stampedLock(final BenchmarkState state) throws
InterruptedException {
long stamp = state.lock.readLock();
try {
if (state.counter.getAndIncrement() < 1000) {
return state.counter.get();
}
} finally {
state.lock.unlockRead(stamp);
}
stamp = state.lock.writeLock();
try {
Thread.sleep(10);
state.counter.set(0);
} finally {
state.lock.unlockWrite(stamp);
}
return state.counter.get();
}
@State(Scope.Benchmark)
public static class BenchmarkState {
private StampedLock lock;
private ReentrantReadWriteLock rwLock;
private AtomicLong counter;
public BenchmarkState() {
}
@Setup(Level.Trial)
public final void setup() {
lock = new StampedLock();
rwLock = new ReentrantReadWriteLock();
counter = new AtomicLong();
}
}
}
Obviously, there is no way the throughput for this benchmarks is greater
than 1000 ops / 10ms = 100 ops/ms. And JMH produces stable results lesser
than 50 ops/ms for 4 threads for both reentrantRwLock and stampedLock.
However, for 32 threads JMH produces sane results only for reentrantRwLock,
but for stampedLock the run looks like this:
# Fork: 1 of 2
# Warmup Iteration 1: 665.471 ops/ms
# Warmup Iteration 2: 782.521 ops/ms
# Warmup Iteration 3: 32.830 ops/ms
# Warmup Iteration 4: 43.356 ops/ms
# Warmup Iteration 5: 2573.165 ops/ms
# Warmup Iteration 6: 408.397 ops/ms
Iteration 1: 9072.582 ops/ms
Iteration 2: 2492.579 ops/ms
# Fork: 2 of 2
# Warmup Iteration 1: 10.445 ops/ms
# Warmup Iteration 2: 27.372 ops/ms
# Warmup Iteration 3: 2632.625 ops/ms
# Warmup Iteration 4: 13.445 ops/ms
# Warmup Iteration 5: 16.657 ops/ms
# Warmup Iteration 6: 28.452 ops/ms
Iteration 1: 30.703 ops/ms
Iteration 2: 27.807 ops/ms
These numbers make no sense because as I mentioned, in these benchmarks the
throughput can never be higher than 100 ops/ms. Is there a valid
explanation for this JMH behaviour, or is this actually a bug in JMH?
Regards,
Valentin
[image: LinkedIn] <https://www.linkedin.com/in/stIncMale> [image: GitHub]
<https://github.com/stIncMale> [image: YouTube]
<https://www.youtube.com/user/stIncMale>
More information about the jmh-dev
mailing list