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.concurrent.TimeUnit;
21  import java.util.function.Supplier;
22  import org.apache.commons.rng.JumpableUniformRandomProvider;
23  import org.apache.commons.rng.LongJumpableUniformRandomProvider;
24  import org.apache.commons.rng.UniformRandomProvider;
25  import org.apache.commons.rng.simple.RandomSource;
26  import org.openjdk.jmh.annotations.Benchmark;
27  import org.openjdk.jmh.annotations.BenchmarkMode;
28  import org.openjdk.jmh.annotations.Fork;
29  import org.openjdk.jmh.annotations.Measurement;
30  import org.openjdk.jmh.annotations.Mode;
31  import org.openjdk.jmh.annotations.OutputTimeUnit;
32  import org.openjdk.jmh.annotations.Param;
33  import org.openjdk.jmh.annotations.Scope;
34  import org.openjdk.jmh.annotations.Setup;
35  import org.openjdk.jmh.annotations.State;
36  import org.openjdk.jmh.annotations.Warmup;
37  
38  /**
39   * Executes benchmark for jump operations of jumpable RNGs.
40   */
41  @BenchmarkMode(Mode.AverageTime)
42  @OutputTimeUnit(TimeUnit.NANOSECONDS)
43  @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
44  @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
45  @State(Scope.Benchmark)
46  @Fork(value = 1, jvmArgs = { "-server", "-Xms128M", "-Xmx128M" })
47  public class JumpBenchmark {
48      /**
49       * Encapsulates a method to jump an RNG.
50       */
51      @State(Scope.Benchmark)
52      public abstract static class BaseJumpableSource {
53          /** The generator of the next RNG copy from a jump. */
54          private Supplier<UniformRandomProvider> gen;
55  
56          /**
57           * Perform a jump.
58           *
59           * @return the value
60           */
61          UniformRandomProvider jump() {
62              return gen.get();
63          }
64  
65          /**
66           * Create the jump function.
67           */
68          @Setup
69          public void setup() {
70              gen = createJumpFunction();
71          }
72  
73          /**
74           * Creates the jump function.
75           * The jump will copy the RNG and then move forward the state of the source
76           * RNG by a large number of steps. The copy is returned.
77           *
78           * @return the copy RNG
79           */
80          protected abstract Supplier<UniformRandomProvider> createJumpFunction();
81      }
82  
83      /**
84       * Exercise the {@link JumpableUniformRandomProvider#jump()} function.
85       */
86      public static class JumpableSource extends BaseJumpableSource {
87          /**
88           * RNG providers.
89           *
90           * <p>Note: Some providers have exactly the same jump method and state size
91           * so are commented out.
92           */
93          @Param({"XOR_SHIFT_1024_S",
94                  //"XOR_SHIFT_1024_S_PHI",
95                  "XO_SHI_RO_128_PLUS",
96                  //"XO_SHI_RO_128_SS",
97                  "XO_RO_SHI_RO_128_PLUS",
98                  //"XO_RO_SHI_RO_128_SS",
99                  "XO_SHI_RO_256_PLUS",
100                 //"XO_SHI_RO_256_SS",
101                 "XO_SHI_RO_512_PLUS",
102                 //"XO_SHI_RO_512_SS",
103                 //"XO_SHI_RO_128_PP",
104                 "XO_RO_SHI_RO_128_PP", // Different update from XO_SHI_RO_128_PLUS
105                 //"XO_SHI_RO_256_PP",
106                 //"XO_SHI_RO_512_PP",
107                 "XO_RO_SHI_RO_1024_PP",
108                 //"XO_RO_SHI_RO_1024_S",
109                 //"XO_RO_SHI_RO_1024_SS",
110                 // Although the LXM jump is the same for all generators with the same LCG
111                 // the performance is different as it captures state copy overhead.
112                 //"L64_X128_SS",
113                 "L64_X128_MIX",
114                 "L64_X256_MIX",
115                 "L64_X1024_MIX",
116                 "L128_X128_MIX",
117                 "L128_X256_MIX",
118                 "L128_X1024_MIX",
119                 "L32_X64_MIX"})
120         private String randomSourceName;
121 
122         /** {@inheritDoc} */
123         @Override
124         protected Supplier<UniformRandomProvider> createJumpFunction() {
125             final UniformRandomProvider rng = RandomSource.valueOf(randomSourceName).create();
126             if (rng instanceof JumpableUniformRandomProvider) {
127                 return ((JumpableUniformRandomProvider) rng)::jump;
128             }
129             throw new IllegalStateException("Invalid jump source: " + randomSourceName);
130         }
131     }
132 
133     /**
134      * Exercise the {@link LongJumpableUniformRandomProvider#longJump()} function.
135      *
136      * <p>Note: Any RNG with a long jump function also has a jump function.
137      * This list should be a subset of {@link JumpableSource}. Testing both methods
138      * is redundant unless the long jump function requires a different routine.
139      * Providers listed here have a different long jump and may be slower/faster
140      * than the corresponding jump function.
141      *
142      * <p>Note: To test other providers the benchmark may be invoked using the
143      * JMH command line:
144      * <pre>
145      * java -jar target/examples-jmh.jar JumpBenchmark.longJump -p randomSourceName=L64_X128_MIX,L128_X128_MIX
146      * </pre>
147      */
148     public static class LongJumpableSource extends BaseJumpableSource {
149         /**
150          * Select RNG providers.
151          */
152         @Param({
153             // Requires the LCG to be advanced 2^32 rather than 1 cycle which
154             // can use precomputed coefficients.
155             "L64_X128_MIX",
156             "L64_X256_MIX",
157             "L64_X1024_MIX",
158             // Requires the LCG to be advanced 2^64 rather than 1 cycle which
159             // leaves the entire lower state unchanged and is computationally simpler
160             // using precomputed coefficients.
161             "L128_X128_MIX",
162             "L128_X256_MIX",
163             "L128_X1024_MIX",
164             // Requires the LCG to be advanced 2^16 rather than 1 cycle which
165             // can use precomputed coefficients.
166             "L32_X64_MIX"})
167         private String randomSourceName;
168 
169 
170         /** {@inheritDoc} */
171         @Override
172         protected Supplier<UniformRandomProvider> createJumpFunction() {
173             final UniformRandomProvider rng = RandomSource.valueOf(randomSourceName).create();
174             if (rng instanceof LongJumpableUniformRandomProvider) {
175                 return ((LongJumpableUniformRandomProvider) rng)::longJump;
176             }
177             throw new IllegalStateException("Invalid long jump source: " + randomSourceName);
178         }
179     }
180 
181     /**
182      * Jump benchmark.
183      *
184      * @param data Source of the jump
185      * @return the copy
186      */
187     @Benchmark
188     public UniformRandomProvider jump(JumpableSource data) {
189         return data.jump();
190     }
191 
192     /**
193      * Long jump benchmark.
194      *
195      * @param data Source of the long jump
196      * @return the copy
197      */
198     @Benchmark
199     public UniformRandomProvider longJump(LongJumpableSource data) {
200         return data.jump();
201     }
202 }