Blackhole.consume(Object) has different semantics to Blackhole.consume(primitive)

Nitsan Wakart nitsanw at yahoo.com
Tue Nov 11 22:46:10 UTC 2014


The primitive consume functions all follow this pattern illustrated below for byte:
    publicvolatilebyteb1, b2;
    ... initialization
        b1 = (byte) r.nextInt(); b2 = (byte) (b1 + 1);
    ...
    public final void consume(byte b) {
        if (b == b1 & b == b2) {
            // SHOULD NEVER HAPPEN
            nullBait.b1= b; // implicit null pointer exception
        }
    }
This serves as a LoadLoad barrier because b1/b2 are volatile loads. performance for all primitive consumes is consistent and effect on benchmark code is uniform. The consume(Object) version is different:
    publicinttlr;
    publicinttlrMask;
    public final void consume(Object obj) {
        int tlr = (this.tlr = (this.tlr * 1664525 + 1013904223));
        if ((tlr & tlrMask) == 0) {
            // SHOULD ALMOST NEVER HAPPEN IN MEASUREMENT
            this.obj1 = obj;
            this.tlrMask = (this.tlrMask << 1) + 1;
        }
    }
There's no volatile load, so the compiler is free to handle subsequent loads differently. There's also the off-chance of a deoptimization if the condition is ever met which can happen in measurement (but really shouldn't happen very often).
I'm not saying one is better than the other, or that the unlikely deoptimization is a massive issue, but the slight semantic differences can lead to surprising effects caused by switching from one consume method to the other. Can we settle on a method? is there a good reason to maintain special treatment?


More information about the jmh-dev mailing list