Usage of Blackhole in a loop distorts benchmark results

Nitsan Wakart nitsanw at yahoo.com
Wed Jan 17 10:40:12 UTC 2018


See this post: http://psy-lob-saw.blogspot.com/2014/08/the-volatile-read-suprise.htmlAnd the current BH code: http://hg.openjdk.java.net/code-tools/jmh/file/a0c4f5e23278/jmh-core/src/main/java/org/openjdk/jmh/infra/Blackhole.java#l306In summary, blackhole carries semantics of calling into an NOT-inlined method and a memory barrier. This is arguably heavy handed, but consider the formidable foe (the compiler) we are trying to fool with it. We are trying to prevent DCE due to unsunk values. It works, yay!The benchmarks you compare with/out blackhole are therefore very different in meaning, and as such different results are not surprising.  

    On Wednesday, January 17, 2018 9:47 AM, Сергей Цыпанов <sergei.tsypanov at yandex.ru> wrote:
 

 Say I have this benchmark:

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Fork(jvmArgsAppend = {"-XX:+UseParallelGC", "-Xms2g", "-Xmx2g"})
public class IteratorFromStreamBenchmark {

    @Benchmark
    public void iteratorFromStream(Data data, Blackhole bh) {
        Iterator<Integer> iterator = data.items.stream()
                .iterator();

        while (iterator.hasNext())
            bh.consume(iterator.next());
    }

    @Benchmark
    public void forEach(Data data, Blackhole bh) {
        data.items.stream().forEach(bh::consume);
    }

    @State(Scope.Thread)
    public static class Data {
        private Collection<Integer> items;

        private int size = 1000;

        @Setup
        public void init() {
            items = IntStream.range(0, size).boxed().collect(toList());
        }
    }
}

which on Java 9 (JDK 9, VM 9+181) yields this output:

Benchmark              Mode  Cnt        Score        Error  Units
forEach                      avgt  100  6130,066 ± 308,597  ns/op
iteratorFromStream    avgt  100  4835,355 ±  57,886  ns/op

Here 'iteratorFromStream' appears to be faster than 'forEach'


Then I change the behaviour to accumulate the result of iteration over elements and return it:

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Fork(jvmArgsAppend = {"-XX:+UseParallelGC", "-Xms2g", "-Xmx2g"})
public class IteratorFromStreamBenchmark {

    @Benchmark
    public int iteratorFromStream(Data data) {
        int sum = 0;
        Iterator<Integer> iterator = data.list.stream()
                .iterator();

        while (iterator.hasNext())
            sum += iterator.next();

        return sum;
    }

    @Benchmark
    public int forEach(Data data) {
        int[] sum = {0};
        data.list.stream().forEach(integer -> sum[0] = sum[0] + integer);
        return sum[0];
    }

    @State(Scope.Thread)
    public static class Data {
        private List<Integer> list;

        private int size = 100;

        @Setup
        public void init() {
            list = IntStream.range(0, size).boxed().collect(toList());
        }
    }
}

Which yields:

Benchmark              Mode  Cnt      Score    Error  Units
forEach                      avgt  100  133,118 ± 1,580  ns/op
iteratorFromStream    avgt  100  228,061 ± 5,491  ns/op


The question here is not only huge difference in absolute values, but the fact 'forEach' now appears to be faster than 'iteratorFromStream'. Also error is lower in case of value returning benchmark.

Could anyone explain is it correct behaviour of Blackhole?

Best regards,
Sergei Tsypanov

   


More information about the jmh-dev mailing list