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 }