1919
2020import java .util .concurrent .TimeUnit ;
2121import java .util .function .Supplier ;
22+ import java .util .function .ToLongFunction ;
23+ import org .apache .commons .rng .ArbitrarilyJumpableUniformRandomProvider ;
2224import org .apache .commons .rng .JumpableUniformRandomProvider ;
2325import org .apache .commons .rng .LongJumpableUniformRandomProvider ;
2426import org .apache .commons .rng .UniformRandomProvider ;
27+ import org .apache .commons .rng .core .source32 .IntProvider ;
2528import org .apache .commons .rng .simple .RandomSource ;
2629import org .openjdk .jmh .annotations .Benchmark ;
2730import org .openjdk .jmh .annotations .BenchmarkMode ;
@@ -50,6 +53,13 @@ public class JumpBenchmark {
5053 */
5154 @ State (Scope .Benchmark )
5255 public abstract static class BaseJumpableSource {
56+ /**
57+ * Distance to skip before jumping.
58+ * Used to advance the state of a generator before repeat jumps.
59+ */
60+ @ Param ({"0" })
61+ private int skip ;
62+
5363 /** The generator of the next RNG copy from a jump. */
5464 private Supplier <UniformRandomProvider > gen ;
5565
@@ -70,6 +80,27 @@ public void setup() {
7080 gen = createJumpFunction ();
7181 }
7282
83+ /**
84+ * Creates the RNG.
85+ *
86+ * @param randomSourceName the random source name
87+ * @return the RNG
88+ */
89+ UniformRandomProvider createRNG (String randomSourceName ) {
90+ final UniformRandomProvider rng = RandomSource .valueOf (randomSourceName ).create ();
91+ if (skip > 0 ) {
92+ // Skip the primary output of the generator.
93+ // Assumes either 32-bit or 64-bit output.
94+ final ToLongFunction <UniformRandomProvider > fun = rng instanceof IntProvider ?
95+ UniformRandomProvider ::nextInt :
96+ UniformRandomProvider ::nextLong ;
97+ for (int i = skip ; --i >= 0 ;) {
98+ fun .applyAsLong (rng );
99+ }
100+ }
101+ return rng ;
102+ }
103+
73104 /**
74105 * Creates the jump function.
75106 * The jump will copy the RNG and then move forward the state of the source
@@ -124,7 +155,7 @@ public static class JumpableSource extends BaseJumpableSource {
124155 /** {@inheritDoc} */
125156 @ Override
126157 protected Supplier <UniformRandomProvider > createJumpFunction () {
127- final UniformRandomProvider rng = RandomSource . valueOf (randomSourceName ). create ( );
158+ final UniformRandomProvider rng = createRNG (randomSourceName );
128159 if (rng instanceof JumpableUniformRandomProvider ) {
129160 return ((JumpableUniformRandomProvider ) rng )::jump ;
130161 }
@@ -172,14 +203,55 @@ public static class LongJumpableSource extends BaseJumpableSource {
172203 /** {@inheritDoc} */
173204 @ Override
174205 protected Supplier <UniformRandomProvider > createJumpFunction () {
175- final UniformRandomProvider rng = RandomSource . valueOf (randomSourceName ). create ( );
206+ final UniformRandomProvider rng = createRNG (randomSourceName );
176207 if (rng instanceof LongJumpableUniformRandomProvider ) {
177208 return ((LongJumpableUniformRandomProvider ) rng )::longJump ;
178209 }
179210 throw new IllegalStateException ("Invalid long jump source: " + randomSourceName );
180211 }
181212 }
182213
214+ /**
215+ * Exercise the {@link ArbitrarilyJumpableUniformRandomProvider#jump(double)} function,
216+ * or the {@link ArbitrarilyJumpableUniformRandomProvider#jumpPowerOfTwo(int)} function.
217+ *
218+ * <p>The power-of-two jump function is called if the distance is an exact {@code int} value.
219+ *
220+ * <p>To jump a small arbitrary amount specify the distance with a fractional component,
221+ * e.g. jump 123 using 123.5, otherwise a power-of-2 jump of 123 will be called.
222+ */
223+ public static class ArbitrarilyJumpableSource extends BaseJumpableSource {
224+ /**
225+ * Select RNG providers.
226+ */
227+ @ Param ({
228+ "PHILOX_4X32" ,
229+ "PHILOX_4X64" })
230+ private String randomSourceName ;
231+
232+ /** Distance to jump.
233+ * Default: 2^99 + 2^49; 2^99 */
234+ @ Param ({"6.338253001141153E29" , "99" })
235+ private double distance ;
236+
237+ /** {@inheritDoc} */
238+ @ Override
239+ protected Supplier <UniformRandomProvider > createJumpFunction () {
240+ final UniformRandomProvider rng = createRNG (randomSourceName );
241+ if (rng instanceof ArbitrarilyJumpableUniformRandomProvider ) {
242+ final ArbitrarilyJumpableUniformRandomProvider gen = (ArbitrarilyJumpableUniformRandomProvider ) rng ;
243+ // Switch to a power-of-2 jump if an int
244+ final int logDistance = (int ) distance ;
245+ if ((double ) logDistance == distance ) {
246+ return () -> gen .jumpPowerOfTwo (logDistance );
247+ }
248+ final double jumpDistance = Math .floor (distance );
249+ return () -> gen .jump (jumpDistance );
250+ }
251+ throw new IllegalStateException ("Invalid arbitrary jump source: " + randomSourceName );
252+ }
253+ }
254+
183255 /**
184256 * Jump benchmark.
185257 *
@@ -194,11 +266,22 @@ public UniformRandomProvider jump(JumpableSource data) {
194266 /**
195267 * Long jump benchmark.
196268 *
197- * @param data Source of the long jump
269+ * @param data Source of the jump
198270 * @return the copy
199271 */
200272 @ Benchmark
201273 public UniformRandomProvider longJump (LongJumpableSource data ) {
202274 return data .jump ();
203275 }
276+
277+ /**
278+ * Arbitrary jump benchmark.
279+ *
280+ * @param data Source of the jump
281+ * @return the copy
282+ */
283+ @ Benchmark
284+ public UniformRandomProvider arbitraryJump (ArbitrarilyJumpableSource data ) {
285+ return data .jump ();
286+ }
204287}
0 commit comments