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 java.util.function.IntSupplier;
21  import org.apache.commons.rng.UniformRandomProvider;
22  import org.apache.commons.rng.core.source64.LongProvider;
23  import org.apache.commons.rng.examples.jmh.RandomSources;
24  import org.apache.commons.rng.simple.RandomSource;
25  import org.openjdk.jmh.annotations.Benchmark;
26  import org.openjdk.jmh.annotations.Param;
27  import org.openjdk.jmh.annotations.Scope;
28  import org.openjdk.jmh.annotations.Setup;
29  import org.openjdk.jmh.annotations.State;
30  
31  /**
32   * Executes a benchmark to compare the speed of generation of random numbers from the
33   * various source providers using the bit cache verses simple generation.
34   */
35  public class CachedNextGenerationPerformance extends AbstractBenchmark {
36      /** The value. Must NOT be final to prevent JVM optimisation! */
37      private boolean booleanValue;
38      /** The value. Must NOT be final to prevent JVM optimisation! */
39      private int intValue;
40  
41      /**
42       * Provides a function to obtain a boolean value from the various "RandomSource"s.
43       * It exercise the default nextBoolean() method (which may use
44       * a cache of bits) against a sign test on the native output.
45       */
46      @State(Scope.Benchmark)
47      public static class BooleanSources extends RandomSources {
48          /** Functional interface for a boolean generator. */
49          interface BooleanSupplier {
50              /**
51               * @return the boolean
52               */
53              boolean getAsBoolean();
54          }
55  
56          /**
57           * The method to create the boolean value.
58           */
59          @Param({"nextBoolean", "signTest"})
60          private String method;
61  
62          /** The generator. */
63          private BooleanSupplier generator;
64  
65          /**
66           * @return the next boolean
67           */
68          boolean next() {
69              return generator.getAsBoolean();
70          }
71  
72          /** {@inheritDoc} */
73          @Override
74          @Setup
75          public void setup() {
76              // Create the generator.
77              super.setup();
78              final UniformRandomProvider rng = getGenerator();
79  
80              // Create the method to generate the boolean
81              if ("signTest".equals(method)) {
82                  if (rng instanceof LongProvider) {
83                      generator = () -> rng.nextLong() < 0;
84                  } else {
85                      // Assumed IntProvider
86                      generator = () -> rng.nextInt() < 0;
87                  }
88              } else if ("nextBoolean".equals(method)) {
89                  // Do not use a method handle 'rng::nextBoolean' for the nextBoolean
90                  // to attempt to maintain a comparable lambda function. The JVM may
91                  // optimise this away.
92                  generator = () -> rng.nextBoolean();
93              } else {
94                  throw new IllegalStateException("Unknown boolean method: " + method);
95              }
96          }
97      }
98      /**
99       * Provides a function to obtain an int value from the various "RandomSource"s
100      * that produce 64-bit output.
101      * It exercise the default nextInt() method (which may use
102      * a cache of bits) against a shift on the native output.
103      */
104     @State(Scope.Benchmark)
105     public static class IntSources {
106         /**
107          * RNG providers. This list is maintained in the order of the {@link RandomSource} enum.
108          *
109          * <p>Include only those that are a {@link LongProvider}.</p>
110          */
111         @Param({"SPLIT_MIX_64",
112                 "XOR_SHIFT_1024_S",
113                 "TWO_CMRES",
114                 "MT_64",
115                 "XOR_SHIFT_1024_S_PHI",
116                 "XO_RO_SHI_RO_128_PLUS",
117                 "XO_RO_SHI_RO_128_SS",
118                 "XO_SHI_RO_256_PLUS",
119                 "XO_SHI_RO_256_SS",
120                 "XO_SHI_RO_512_PLUS",
121                 "XO_SHI_RO_512_SS",
122                 "PCG_RXS_M_XS_64",
123                 "SFC_64",
124                 "JSF_64",
125                 "XO_RO_SHI_RO_128_PP",
126                 "XO_SHI_RO_256_PP",
127                 "XO_SHI_RO_512_PP",
128                 "XO_RO_SHI_RO_1024_PP",
129                 "XO_RO_SHI_RO_1024_S",
130                 "XO_RO_SHI_RO_1024_SS",
131                 "PCG_RXS_M_XS_64_OS"})
132         private String randomSourceName;
133 
134         /**
135          * The method to create the int value.
136          */
137         @Param({"nextInt", "shiftLong"})
138         private String method;
139 
140         /** The generator. */
141         private IntSupplier gen;
142 
143         /**
144          * @return the next int
145          */
146         int next() {
147             return gen.getAsInt();
148         }
149 
150         /** Create the int source. */
151         @Setup
152         public void setup() {
153             final UniformRandomProvider rng = RandomSource.valueOf(randomSourceName).create();
154             if (!(rng instanceof LongProvider)) {
155                 throw new IllegalStateException("Not a LongProvider: " + rng.getClass().getName());
156             }
157 
158             // Create the method to generate the int
159             if ("shiftLong".equals(method)) {
160                 gen = () -> (int) (rng.nextLong() >>> 32);
161             } else if ("nextInt".equals(method)) {
162                 // Do not use a method handle 'rng::nextInt' for the nextInt
163                 // to attempt to maintain a comparable lambda function. The JVM may
164                 // optimise this away.
165                 gen = () -> rng.nextInt();
166             } else {
167                 throw new IllegalStateException("Unknown int method: " + method);
168             }
169         }
170     }
171 
172     /**
173      * Baseline for a JMH method call with no return value.
174      */
175     @Benchmark
176     public void baselineVoid() {
177         // Do nothing, this is a baseline
178     }
179 
180     /**
181      * Baseline for a JMH method call returning a {@code boolean}.
182      *
183      * @return the value
184      */
185     @Benchmark
186     public boolean baselineBoolean() {
187         return booleanValue;
188     }
189 
190     /**
191      * Baseline for a JMH method call returning an {@code int}.
192      *
193      * @return the value
194      */
195     @Benchmark
196     public int baselineInt() {
197         return intValue;
198     }
199 
200     /**
201      * Exercise the boolean generation method.
202      *
203      * @param sources Source of randomness.
204      * @return the boolean
205      */
206     @Benchmark
207     public boolean nextBoolean(BooleanSources sources) {
208         return sources.next();
209     }
210 
211     /**
212      * Exercise the int generation method.
213      *
214      * @param sources Source of randomness.
215      * @return the int
216      */
217     @Benchmark
218     public int nextInt(IntSources sources) {
219         return sources.next();
220     }
221 }