blob: 1fdc0672058cd6167fc2f7a1ae9ba62c692f21ee [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.rng.examples.jmh.core;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
/**
* Executes benchmark to compare the speed of generation of floating point
* numbers from the integer primitives.
*/
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@State(Scope.Benchmark)
@Fork(value = 1, jvmArgs = { "-server", "-Xms128M", "-Xmx128M" })
public class FloatingPointGenerationPerformance {
/**
* Mimic the generation of the SplitMix64 algorithm.
*
* <p>The final mixing step must be included otherwise the output numbers are sequential
* and the test may run with a lack of numbers with higher order bits.</p>
*/
@State(Scope.Benchmark)
public static class LongSource {
/** The state. */
private long state = ThreadLocalRandom.current().nextLong();
/**
* Get the next long.
*
* @return the long
*/
public final long nextLong() {
long z = state += 0x9e3779b97f4a7c15L;
z = (z ^ (z >>> 30)) * 0xbf58476d1ce4e5b9L;
z = (z ^ (z >>> 27)) * 0x94d049bb133111ebL;
return z ^ (z >>> 31);
}
/**
* Get the next int.
*
* <p>Returns the 32 high bits of Stafford variant 4 mix64 function as int.
*
* @return the int
*/
public final int nextInt() {
long z = state += 0x9e3779b97f4a7c15L;
z = (z ^ (z >>> 33)) * 0x62a9d9ed799705f5L;
return (int)(((z ^ (z >>> 28)) * 0xcb24d0a5c88c35b3L) >>> 32);
}
}
// Benchmark methods
/**
* @param source the source
* @return the long
*/
@Benchmark
public long nextDoubleBaseline(LongSource source) {
return source.nextLong();
}
/**
* @param source the source
* @return the double
*/
@Benchmark
public double nextDoubleUsingBitsToDouble(LongSource source) {
// Combine 11 bit unsigned exponent with value 1023 (768 + 255) with 52 bit mantissa
// 0x300L = 256 + 512 = 768
// 0x0ff = 255
// This makes a number in the range 1.0 to 2.0 so subtract 1.0
return Double.longBitsToDouble(0x3ffL << 52 | source.nextLong() >>> 12) - 1.0;
}
/**
* @param source the source
* @return the double
*/
@Benchmark
public double nextDoubleUsingMultiply52bits(LongSource source) {
return (source.nextLong() >>> 12) * 0x1.0p-52d; // 1.0 / (1L << 52)
}
/**
* @param source the source
* @return the double
*/
@Benchmark
public double nextDoubleUsingMultiply53bits(LongSource source) {
return (source.nextLong() >>> 11) * 0x1.0p-53d; // 1.0 / (1L << 53)
}
/**
* @param source the source
* @return the int
*/
@Benchmark
public int nextFloatBaseline(LongSource source) {
return source.nextInt();
}
/**
* @param source the source
* @return the float
*/
@Benchmark
public float nextFloatUsingBitsToFloat(LongSource source) {
// Combine 8 bit unsigned exponent with value 127 (112 + 15) with 23 bit mantissa
// 0x70 = 64 + 32 + 16 = 112
// 0x0f = 15
// This makes a number in the range 1.0f to 2.0f so subtract 1.0f
return Float.intBitsToFloat(0x7f << 23 | source.nextInt() >>> 9) - 1.0f;
}
/**
* @param source the source
* @return the float
*/
@Benchmark
public float nextFloatUsingMultiply23bits(LongSource source) {
return (source.nextInt() >>> 9) * 0x1.0p-23f; // 1.0f / (1 << 23)
}
/**
* @param source the source
* @return the float
*/
@Benchmark
public float nextFloatUsingMultiply24bits(LongSource source) {
return (source.nextInt() >>> 8) * 0x1.0p-24f; // 1.0f / (1 << 24)
}
}