I am trying to write a method like this:
static boolean fitsInDouble(long x) {
And I'm trying to find the most efficient implementation. I settled on one, but then a colleague ran tests and got different relative results. The fastest implementation for me is not the fastest for him.
Is there something wrong with these tests?
package rnd; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; import org.openjdk.jmh.annotations.Measurement; import org.openjdk.jmh.annotations.Mode; import org.openjdk.jmh.annotations.OutputTimeUnit; import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Warmup; import org.openjdk.jmh.infra.Blackhole; import org.openjdk.jmh.runner.Runner; import org.openjdk.jmh.runner.options.Options; import org.openjdk.jmh.runner.options.OptionsBuilder; import java.math.BigDecimal; import java.util.concurrent.TimeUnit; @State(Scope.Thread) @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) @Fork(1) @Measurement(iterations = 5) @Warmup(iterations = 5) public class Benchmarks { public static void main(String[] args) throws Exception { Options options = new OptionsBuilder() .include(Benchmarks.class.getName()) .build(); new Runner(options).run(); } @Benchmark public void bigDecimal(Blackhole bh) { for (long x : NUMBERS) bh.consume(bigDecimal(x)); } @Benchmark public void cast(Blackhole bh) { for (long x : NUMBERS) bh.consume(cast(x)); } @Benchmark public void zeros(Blackhole bh) { for (long x : NUMBERS) bh.consume(zeros(x)); } public static boolean bigDecimal(long x) { BigDecimal a = new BigDecimal(x); BigDecimal b = new BigDecimal((double) x); return a.compareTo(b) == 0; } public static boolean cast(long x) { return x == (long) (double) x && x != Long.MAX_VALUE; } public static boolean zeros(long x) { long a = Math.abs(x); int z = Long.numberOfLeadingZeros(a); return z > 10 || Long.numberOfTrailingZeros(a) > 10 - z; } private static final long[] NUMBERS = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, 123, 456, 789, -123, -456, -789, 101112, 131415, 161718, -101112, -131415, -161718, 11L, 222L, 3333L, 44444L, 555555L, 6666666L, 77777777L, 888888888L, 9999999999L, 1111L, 22222L, 333333L, 4444444L, 55555555L, 666666666L, 7777777777L, 88888888888L, 999999999999L, 11111111, 222222222, 3333333333L, 44444444444L, 555555555555L, 6666666666666L, 77777777777777L, 888888888888888L, 9999999999999999L, Long.MAX_VALUE, Long.MAX_VALUE - 1, Long.MIN_VALUE, Long.MIN_VALUE + 1, (1L << 53), (1l << 53) + 1, (1l << 53) + 2, (1l << 60), (1l << 60) + 1, (1l << 60) + 8, (1l << 60) + 32, (1l << 60) + 64, (1l << 60) + 128, (1l << 60) + 256, (-1L << 53), (-1L << 53) - 1, (-1L << 53) - 2, (-1l << 60), (-1l << 60) - 1, (-1l << 60) - 8, (-1l << 60) - 32, (-1l << 60) - 64, (-1l << 60) - 128, (-1l << 60) - 256 }; }
There are small differences in our environment.
Me: Windows 10, JDK 1.8.0_45, zeros are the fastest
Him: Windows 7, JDK 1.8.0_20, "cast" is the fastest
Our results are self-consistent from run to run, whether working in the IDE or from the command line. We use JMH 1.10.5.
What's going on here? This test seems unreliable and I don't know how to fix it.