View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.rng.examples.jmh.core;
19  
20  import org.openjdk.jmh.annotations.Benchmark;
21  import org.openjdk.jmh.annotations.BenchmarkMode;
22  import org.openjdk.jmh.annotations.Mode;
23  import org.openjdk.jmh.annotations.Warmup;
24  import org.openjdk.jmh.annotations.Measurement;
25  import org.openjdk.jmh.annotations.State;
26  import org.openjdk.jmh.annotations.Fork;
27  import org.openjdk.jmh.annotations.Scope;
28  import org.openjdk.jmh.annotations.OutputTimeUnit;
29  
30  import java.util.concurrent.ThreadLocalRandom;
31  import java.util.concurrent.TimeUnit;
32  
33  /**
34   * Executes benchmark to compare the speed of generation of floating point
35   * numbers from the integer primitives.
36   */
37  @BenchmarkMode(Mode.Throughput)
38  @OutputTimeUnit(TimeUnit.MICROSECONDS)
39  @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
40  @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
41  @State(Scope.Benchmark)
42  @Fork(value = 1, jvmArgs = { "-server", "-Xms128M", "-Xmx128M" })
43  public class FloatingPointGenerationPerformance {
44      /**
45       * Mimic the generation of the SplitMix64 algorithm.
46       *
47       * <p>The final mixing step must be included otherwise the output numbers are sequential
48       * and the test may run with a lack of numbers with higher order bits.</p>
49       */
50      @State(Scope.Benchmark)
51      public static class LongSource {
52          /** The state. */
53          private long state = ThreadLocalRandom.current().nextLong();
54  
55          /**
56           * Get the next long.
57           *
58           * @return the long
59           */
60          public final long nextLong() {
61              long z = state += 0x9e3779b97f4a7c15L;
62              z = (z ^ (z >>> 30)) * 0xbf58476d1ce4e5b9L;
63              z = (z ^ (z >>> 27)) * 0x94d049bb133111ebL;
64              return z ^ (z >>> 31);
65          }
66  
67          /**
68           * Get the next int.
69           *
70           * <p>Returns the 32 high bits of Stafford variant 4 mix64 function as int.
71           *
72           * @return the int
73           */
74          public final int nextInt() {
75              long z = state += 0x9e3779b97f4a7c15L;
76              z = (z ^ (z >>> 33)) * 0x62a9d9ed799705f5L;
77              return (int)(((z ^ (z >>> 28)) * 0xcb24d0a5c88c35b3L) >>> 32);
78          }
79      }
80  
81      // Benchmark methods
82  
83      /**
84       * @param source the source
85       * @return the long
86       */
87      @Benchmark
88      public long nextDoubleBaseline(LongSource source) {
89          return source.nextLong();
90      }
91  
92      /**
93       * @param source the source
94       * @return the double
95       */
96      @Benchmark
97      public double nextDoubleUsingBitsToDouble(LongSource source) {
98          // Combine 11 bit unsigned exponent with value 1023 (768 + 255) with 52 bit mantissa
99          // 0x300L = 256 + 512 = 768
100         // 0x0ff  = 255
101         // This makes a number in the range 1.0 to 2.0 so subtract 1.0
102         return Double.longBitsToDouble(0x3ffL << 52 | source.nextLong() >>> 12) - 1.0;
103     }
104 
105     /**
106      * @param source the source
107      * @return the double
108      */
109     @Benchmark
110     public double nextDoubleUsingMultiply52bits(LongSource source) {
111         return (source.nextLong() >>> 12) * 0x1.0p-52d; // 1.0 / (1L << 52)
112     }
113 
114     /**
115      * @param source the source
116      * @return the double
117      */
118     @Benchmark
119     public double nextDoubleUsingMultiply53bits(LongSource source) {
120         return (source.nextLong() >>> 11) * 0x1.0p-53d; // 1.0 / (1L << 53)
121     }
122 
123     /**
124      * @param source the source
125      * @return the int
126      */
127     @Benchmark
128     public int nextFloatBaseline(LongSource source) {
129         return source.nextInt();
130     }
131 
132     /**
133      * @param source the source
134      * @return the float
135      */
136     @Benchmark
137     public float nextFloatUsingBitsToFloat(LongSource source) {
138         // Combine 8 bit unsigned exponent with value 127 (112 + 15) with 23 bit mantissa
139         // 0x70 = 64 + 32 + 16 = 112
140         // 0x0f = 15
141         // This makes a number in the range 1.0f to 2.0f so subtract 1.0f
142         return Float.intBitsToFloat(0x7f << 23 | source.nextInt() >>> 9) - 1.0f;
143     }
144 
145     /**
146      * @param source the source
147      * @return the float
148      */
149     @Benchmark
150     public float nextFloatUsingMultiply23bits(LongSource source) {
151         return (source.nextInt() >>> 9) * 0x1.0p-23f; // 1.0f / (1 << 23)
152     }
153 
154     /**
155      * @param source the source
156      * @return the float
157      */
158     @Benchmark
159     public float nextFloatUsingMultiply24bits(LongSource source) {
160         return (source.nextInt() >>> 8) * 0x1.0p-24f; // 1.0f / (1 << 24)
161     }
162 }