changeset 101:8046f2c4fce7

Blackholes: improve symmetry for different data types and values.
author shade
date Wed, 17 Jul 2013 15:14:00 +0400
parents 1f2690d966ec
children 62e726a16873
files jmh-core-benchmarks/src/main/java/org/openjdk/jmh/benchmarks/BlackholeBench.java jmh-core/src/main/java/org/openjdk/jmh/logic/BlackHole.java jmh-core/src/test/java/org/openjdk/jmh/logic/BlackholeTest.java
diffstat 3 files changed, 193 insertions(+), 84 deletions(-) [+]
line wrap: on
line diff
--- a/jmh-core-benchmarks/src/main/java/org/openjdk/jmh/benchmarks/BlackholeBench.java	Fri Jul 12 00:18:10 2013 +0400
+++ b/jmh-core-benchmarks/src/main/java/org/openjdk/jmh/benchmarks/BlackholeBench.java	Wed Jul 17 15:14:00 2013 +0400
@@ -65,6 +65,9 @@
     public Object o;
     public Object[] os;
 
+    public boolean trueBoolean = true;
+    public boolean falseBoolean = false;
+
     @GenerateMicroBenchmark
     public void baseline() {
         // do nothing
@@ -81,6 +84,26 @@
     }
 
     @GenerateMicroBenchmark
+    public boolean implicit_testBoolean_true() {
+        return true;
+    }
+
+    @GenerateMicroBenchmark
+    public boolean implicit_testBoolean_false() {
+        return false;
+    }
+
+    @GenerateMicroBenchmark
+    public boolean implicit_testBoolean_trueF() {
+        return trueBoolean;
+    }
+
+    @GenerateMicroBenchmark
+    public boolean implicit_testBoolean_falseF() {
+        return falseBoolean;
+    }
+
+    @GenerateMicroBenchmark
     public char implicit_testChar() {
         return c;
     }
--- a/jmh-core/src/main/java/org/openjdk/jmh/logic/BlackHole.java	Fri Jul 12 00:18:10 2013 +0400
+++ b/jmh-core/src/main/java/org/openjdk/jmh/logic/BlackHole.java	Wed Jul 17 15:14:00 2013 +0400
@@ -42,19 +42,18 @@
 }
 
 class BlackHoleL2 extends BlackholeL1 {
-    public volatile byte b1 = 1, b2 = 2;
-    public volatile boolean bool1 = false, bool2 = true;
-    public volatile char c1 = 'A', c2 = 'B';
-    public volatile short s1 = 1, s2 = 2;
-    public volatile int i1 = 1, i2 = 2;
-    public volatile long l1 = 1, l2 = 2;
-    public volatile float f1 = 1.0f, f2 = 2.0f;
-    public volatile double d1 = 1.0d, d2 = 2.0d;
-    public volatile Object obj1 = new Object();
-    public volatile Object[] objs1 = new Object[]{new Object()};
-    public volatile BlackHoleL2 nullBait = null;
-    public long tlr = System.nanoTime();
-    public long tlrMask = 1;
+    public byte b1;
+    public boolean bool1;
+    public char c1;
+    public short s1;
+    public int i1;
+    public long l1;
+    public float f1;
+    public double d1;
+    public Object obj1;
+    public Object[] objs1;
+    public int tlr = (int)System.nanoTime(); // randomize
+    public int tlrMask = 1;
 }
 
 class BlackHoleL3 extends BlackHoleL2 {
@@ -112,33 +111,21 @@
      * We also pad with "int"-s so that dense layout in superclass does not
      * have the gap where runtime can fit the subclass field.
      * <p/>
-     * 2. Compilers are unable to predict the value of the volatile read.
-     * While the compilers can speculatively optimize until the relevant
-     * volatile write happens, it is unlikely to be practical to be able to stop
-     * all the threads the instant that write had happened.
+     * We fight the dead-code elimination by storing the result on the heap.
+     * Since heap writes are expensive (notably for objects which entail
+     * store barrier), we are using the inlined PRNG to store only every once
+     * in a while. The compilers can eliminate the slow path until it was hit,
+     * and move more computation under the branch in consume(), so we need
+     * to warm up the slow-path branch. To do that, we gradually increase the
+     * range over which we find the collision with zero for the PRNG.
      * <p/>
-     * This allows us to compare the incoming values against the relevant
-     * volatile fields. The values in those volatile fields are never changing,
-     * but due to (2), we should re-read the values again and again.
-     * <p/>
-     * Primitives are a bit hard, because we can't predict what values we
-     * will be fed. But we can compare the incoming value with *two* distinct
-     * known values, and both checks will never be true at the same time.
-     * Note the bitwise AND in all the predicates: both to spare additional
-     * branch, and also to provide more uniformity in the performance.
-     * <p/>
-     * Objects should normally abide the Java's referential semantics, i.e. the
-     * incoming objects will never be equal to the distinct object we have, and
-     * volatile read will break the speculation about what we compare with.
-     * However, smart compilers may deduce that the distinct non-escaped object
-     * on the other side is not equal to anything we have, and fold the comparison
-     * to "false". We do inlined thread-local random to get those objects escaped
-     * with infinitesimal probability. Then again, smart compilers may skip from
-     * generating the slow path, and apply the previous logic to constant-fold
-     * the condition to "false". We are warming up the slow-path in the beginning
-     * to evade that effect.
+     * Note that using PRNG still induces the heap writes, but those writes
+     * are consistent for every consumed data type. We use also the linear
+     * congruential generator with the glibc/gcc constants, which yields
+     * enough randomicity in minor bits to tolerate increasing bit masks
+     * (which is similar to increasing modulo), and operates on 32-bit values
+     * (which decreases the register pressure on 32-bit VMs).
      */
-
     private static Unsafe U;
 
     static {
@@ -158,21 +145,13 @@
     static void consistencyCheck() {
         // checking the fields are not reordered
         check("b1");
-        check("b2");
         check("bool1");
-        check("bool2");
         check("c1");
-        check("c2");
         check("s1");
-        check("s2");
         check("i1");
-        check("i2");
         check("l1");
-        check("l2");
         check("f1");
-        check("f2");
         check("d1");
-        check("d2");
         check("obj1");
         check("objs1");
     }
@@ -202,13 +181,13 @@
      */
     public final void consume(Object obj) {
         // let's play the optimizing compiler, dude!
-        long tlr = this.tlr;
-        long tlrMask = this.tlrMask;
+        int tlr = this.tlr;
+        int tlrMask = this.tlrMask;
 
-        this.tlr = (tlr * 0x5DEECE66DL + 0xBL) & (0xFFFFFFFFFFFFL);
-        if ((tlr & tlrMask) == 0) {
+        this.tlr = (tlr * 1103515245 + 12345) & tlrMask;
+        if (tlr == 0) {
             // SHOULD ALMOST NEVER HAPPEN IN MEASUREMENT
-            if (tlrMask != 0x7FFFFFFFFFFFFFFFL) {
+            if (tlrMask != 0x7FFFFFFF) {
                 this.tlrMask = (tlrMask << 1) + 1;
             }
             this.obj1 = obj;
@@ -221,14 +200,13 @@
      * @param objs objects to consume.
      */
     public final void consume(Object[] objs) {
-        // let's play the optimizing compiler, dude!
-        long tlr = this.tlr;
-        long tlrMask = this.tlrMask;
+        int tlr = this.tlr;
+        int tlrMask = this.tlrMask;
 
-        this.tlr = (tlr * 0x5DEECE66DL + 0xBL) & (0xFFFFFFFFFFFFL);
-        if ((tlr & tlrMask) == 0) {
+        this.tlr = (tlr * 1103515245 + 12345) & tlrMask;
+        if (tlr == 0) {
             // SHOULD ALMOST NEVER HAPPEN IN MEASUREMENT
-            if (tlrMask != 0x7FFFFFFFFFFFFFFFL) {
+            if (tlrMask != 0x7FFFFFFF) {
                 this.tlrMask = (tlrMask << 1) + 1;
             }
             this.objs1 = objs;
@@ -241,9 +219,16 @@
      * @param b object to consume.
      */
     public final void consume(byte b) {
-        if (b == b1 & b == b2) {
-            // SHOULD NEVER HAPPEN
-            nullBait.b1 = b; // implicit null pointer exception
+        int tlr = this.tlr;
+        int tlrMask = this.tlrMask;
+
+        this.tlr = (tlr * 1103515245 + 12345) & tlrMask;
+        if (tlr == 0) {
+            // SHOULD ALMOST NEVER HAPPEN IN MEASUREMENT
+            if (tlrMask != 0x7FFFFFFF) {
+                this.tlrMask = (tlrMask << 1) + 1;
+            }
+            this.b1 = b;
         }
     }
 
@@ -253,9 +238,16 @@
      * @param bool object to consume.
      */
     public final void consume(boolean bool) {
-        if (bool == bool1 & bool == bool2) {
-            // SHOULD NEVER HAPPEN
-            nullBait.bool1 = bool; // implicit null pointer exception
+        int tlr = this.tlr;
+        int tlrMask = this.tlrMask;
+
+        this.tlr = (tlr * 1103515245 + 12345) & tlrMask;
+        if (tlr == 0) {
+            // SHOULD ALMOST NEVER HAPPEN IN MEASUREMENT
+            if (tlrMask != 0x7FFFFFFF) {
+                this.tlrMask = (tlrMask << 1) + 1;
+            }
+            this.bool1 = bool;
         }
     }
 
@@ -265,9 +257,16 @@
      * @param c object to consume.
      */
     public final void consume(char c) {
-        if (c == c1 & c == c2) {
-            // SHOULD NEVER HAPPEN
-            nullBait.c1 = c; // implicit null pointer exception
+        int tlr = this.tlr;
+        int tlrMask = this.tlrMask;
+
+        this.tlr = (tlr * 1103515245 + 12345) & tlrMask;
+        if (tlr == 0) {
+            // SHOULD ALMOST NEVER HAPPEN IN MEASUREMENT
+            if (tlrMask != 0x7FFFFFFF) {
+                this.tlrMask = (tlrMask << 1) + 1;
+            }
+            this.c1 = c;
         }
     }
 
@@ -277,9 +276,16 @@
      * @param s object to consume.
      */
     public final void consume(short s) {
-        if (s == s1 & s == s2) {
-            // SHOULD NEVER HAPPEN
-            nullBait.s1 = s; // implicit null pointer exception
+        int tlr = this.tlr;
+        int tlrMask = this.tlrMask;
+
+        this.tlr = (tlr * 1103515245 + 12345) & tlrMask;
+        if (tlr == 0) {
+            // SHOULD ALMOST NEVER HAPPEN IN MEASUREMENT
+            if (tlrMask != 0x7FFFFFFF) {
+                this.tlrMask = (tlrMask << 1) + 1;
+            }
+            this.s1 = s;
         }
     }
 
@@ -289,9 +295,16 @@
      * @param i object to consume.
      */
     public final void consume(int i) {
-        if (i == i1 & i == i2) {
-            // SHOULD NEVER HAPPEN
-            nullBait.i1 = i; // implicit null pointer exception
+        int tlr = this.tlr;
+        int tlrMask = this.tlrMask;
+
+        this.tlr = (tlr * 1103515245 + 12345) & tlrMask;
+        if (tlr == 0) {
+            // SHOULD ALMOST NEVER HAPPEN IN MEASUREMENT
+            if (tlrMask != 0x7FFFFFFF) {
+                this.tlrMask = (tlrMask << 1) + 1;
+            }
+            this.i1 = i;
         }
     }
 
@@ -301,9 +314,16 @@
      * @param l object to consume.
      */
     public final void consume(long l) {
-        if (l == l1 & l == l2) {
-            // SHOULD NEVER HAPPEN
-            nullBait.l1 = l; // implicit null pointer exception
+        int tlr = this.tlr;
+        int tlrMask = this.tlrMask;
+
+        this.tlr = (tlr * 1103515245 + 12345) & tlrMask;
+        if (tlr == 0) {
+            // SHOULD ALMOST NEVER HAPPEN IN MEASUREMENT
+            if (tlrMask != 0x7FFFFFFF) {
+                this.tlrMask = (tlrMask << 1) + 1;
+            }
+            this.l1 = l;
         }
     }
 
@@ -313,9 +333,16 @@
      * @param f object to consume.
      */
     public final void consume(float f) {
-        if (f == f1 & f == f2) {
-            // SHOULD NEVER HAPPEN
-            nullBait.f1 = f; // implicit null pointer exception
+        int tlr = this.tlr;
+        int tlrMask = this.tlrMask;
+
+        this.tlr = (tlr * 1103515245 + 12345) & tlrMask;
+        if (tlr == 0) {
+            // SHOULD ALMOST NEVER HAPPEN IN MEASUREMENT
+            if (tlrMask != 0x7FFFFFFF) {
+                this.tlrMask = (tlrMask << 1) + 1;
+            }
+            this.f1 = f;
         }
     }
 
@@ -325,13 +352,20 @@
      * @param d object to consume.
      */
     public final void consume(double d) {
-        if (d == d1 & d == d2) {
-            // SHOULD NEVER HAPPEN
-            nullBait.d1 = d; // implicit null pointer exception
+        int tlr = this.tlr;
+        int tlrMask = this.tlrMask;
+
+        this.tlr = (tlr * 1103515245 + 12345) & tlrMask;
+        if (tlr == 0) {
+            // SHOULD ALMOST NEVER HAPPEN IN MEASUREMENT
+            if (tlrMask != 0x7FFFFFFF) {
+                this.tlrMask = (tlrMask << 1) + 1;
+            }
+            this.d1 = d;
         }
     }
 
-    public static volatile long consumedCPU = 42;
+    public static volatile int consumedCPU = 42;
 
     /**
      * Consume some amount of time tokens.
@@ -343,10 +377,10 @@
      */
     public static void consumeCPU(long tokens) {
         // randomize start so that JIT could not memoize;
-        long t = consumedCPU;
+        int t = consumedCPU;
 
         for (long i = 0; i < tokens; i++) {
-            t += (t * 0x5DEECE66DL + 0xBL) & (0xFFFFFFFFFFFFL);
+            t += (t * 1103515245 + 12345);
         }
 
         // need to guarantee side-effect on the result,
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core/src/test/java/org/openjdk/jmh/logic/BlackholeTest.java	Wed Jul 17 15:14:00 2013 +0400
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.logic;
+
+import junit.framework.Assert;
+import org.junit.Test;
+
+public class BlackholeTest {
+
+    @Test
+    public void generatorTest() {
+        int tlr = (int) System.nanoTime();
+        int tlrMask = 0;
+
+        int flips = 0;
+
+        for (int i = 0; i < Integer.MAX_VALUE; i++) {
+            tlr = (tlr * 1103515245 + 12345) & tlrMask;
+            if (tlr == 0) {
+                if (tlrMask != 0x7FFFFFFF) {
+                    tlrMask = (tlrMask << 1) + 1;
+                }
+                flips++;
+            }
+        }
+
+        Assert.assertEquals(31, flips); // flipped for every tlrMask
+    }
+
+}