This is an automated email from the ASF dual-hosted git repository.

aherbert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-rng.git


The following commit(s) were added to refs/heads/master by this push:
     new e37e90e4 RNG-187: Add benchmarks for shuffle without index checks
e37e90e4 is described below

commit e37e90e463f67d186fa68dd501f1c24a31a08cb1
Author: Alex Herbert <aherb...@apache.org>
AuthorDate: Mon Aug 26 22:14:38 2024 +0100

    RNG-187: Add benchmarks for shuffle without index checks
---
 .../jmh/sampling/ArrayShuffleBenchmark.java        | 57 ++++++++++++++++++++--
 .../jmh/sampling/ArrayShuffleBenchmarkTest.java    | 20 ++++++++
 2 files changed, 73 insertions(+), 4 deletions(-)

diff --git 
a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/sampling/ArrayShuffleBenchmark.java
 
b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/sampling/ArrayShuffleBenchmark.java
index 419b44d0..63a84652 100644
--- 
a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/sampling/ArrayShuffleBenchmark.java
+++ 
b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/sampling/ArrayShuffleBenchmark.java
@@ -152,7 +152,10 @@ public class ArrayShuffleBenchmark {
         /**
          * Method name.
          */
-        @Param({"shuffle", "shuffle2", "shuffle3", "shuffle4"})
+        @Param({"shuffle1",
+            // Effectively the same speed as shuffle1
+            //"shuffle1a",
+            "shuffle2", "shuffle3", "shuffle4"})
         private String method;
 
         /** Shuffle function. */
@@ -172,8 +175,10 @@ public class ArrayShuffleBenchmark {
          */
         @Setup
         public void setup() {
-            if ("shuffle".equals(method)) {
+            if ("shuffle1".equals(method)) {
                 fun = ArrayShuffleBenchmark::shuffle1;
+            } else if ("shuffle1a".equals(method)) {
+                fun = ArrayShuffleBenchmark::shuffle1a;
             } else if ("shuffle2".equals(method)) {
                 fun = ArrayShuffleBenchmark::shuffle2;
             } else if ("shuffle3".equals(method)) {
@@ -214,6 +219,50 @@ public class ArrayShuffleBenchmark {
         return array;
     }
 
+    /**
+     * Generates an {@code int} value between 0 (inclusive) and the specified 
value
+     * (exclusive).
+     *
+     * <p>Taken from {@code o.a.c.rng.UniformRandomProviderSupport}. This is 
used
+     * to benchmark elimination of the conditional check for a negative index 
in
+     * {@link UniformRandomProvider#nextInt(int)}.
+     *
+     * @param source Source of randomness.
+     * @param n Bound on the random number to be returned. Must be strictly 
positive.
+     * @return a random {@code int} value between 0 (inclusive) and {@code n} 
(exclusive).
+     */
+    static int nextInt(UniformRandomProvider source,
+                       int n) {
+        // Lemire (2019): Fast Random Integer Generation in an Interval
+        // https://arxiv.org/abs/1805.10941
+        long m = (source.nextInt() & 0xffffffffL) * n;
+        long l = m & 0xffffffffL;
+        if (l < n) {
+            // 2^32 % n
+            final long t = POW_32 % n;
+            while (l < t) {
+                m = (source.nextInt() & 0xffffffffL) * n;
+                l = m & 0xffffffffL;
+            }
+        }
+        return (int) (m >>> 32);
+    }
+
+    /**
+     * Shuffles the entries of the given array.
+     * Uses a Fisher-Yates shuffle.
+     *
+     * @param rng Source of randomness.
+     * @param array Array whose entries will be shuffled (in-place).
+     * @return a reference to the given array
+     */
+    static int[] shuffle1a(UniformRandomProvider rng, int[] array) {
+        for (int i = array.length; i > 1; i--) {
+            swap(array, i - 1, nextInt(rng, i));
+        }
+        return array;
+    }
+
     /**
      * Return two random values in {@code [0, range1)} and {@code [0, 
range2)}. The
      * product bound is used for the reject algorithm. See Brackett-Rozinsky 
and Lemire.
@@ -283,7 +332,7 @@ public class ArrayShuffleBenchmark {
     }
 
     /**
-     * Return two random values in {@code [0, range1)}, {@code [0, range2)}
+     * Return three random values in {@code [0, range1)}, {@code [0, range2)}
      * and {@code [0, range3)}. The
      * product bound is used for the reject algorithm. See Brackett-Rozinsky 
and Lemire.
      *
@@ -372,7 +421,7 @@ public class ArrayShuffleBenchmark {
     }
 
     /**
-     * Return two random values in {@code [0, range1)}, {@code [0, range2)},
+     * Return four random values in {@code [0, range1)}, {@code [0, range2)},
      * {@code [0, range3)} and {@code [0, range4)}. The
      * product bound is used for the reject algorithm. See Brackett-Rozinsky 
and Lemire.
      *
diff --git 
a/commons-rng-examples/examples-jmh/src/test/java/org/apache/commons/rng/examples/jmh/sampling/ArrayShuffleBenchmarkTest.java
 
b/commons-rng-examples/examples-jmh/src/test/java/org/apache/commons/rng/examples/jmh/sampling/ArrayShuffleBenchmarkTest.java
index 4922ee7e..7a105824 100644
--- 
a/commons-rng-examples/examples-jmh/src/test/java/org/apache/commons/rng/examples/jmh/sampling/ArrayShuffleBenchmarkTest.java
+++ 
b/commons-rng-examples/examples-jmh/src/test/java/org/apache/commons/rng/examples/jmh/sampling/ArrayShuffleBenchmarkTest.java
@@ -125,4 +125,24 @@ class ArrayShuffleBenchmarkTest {
         final double p = new ChiSquareTest().chiSquareTest(observed);
         Assertions.assertFalse(p < 1e-3, () -> "p-value too small: " + p);
     }
+
+    @ParameterizedTest
+    @CsvSource({
+        "257",
+        "8073",
+    })
+    void testShuffle1a(int length) {
+        final int[] a = PermutationSampler.natural(length);
+        final int[] b = a.clone();
+        final RandomSource source = RandomSource.XO_RO_SHI_RO_128_PP;
+        final byte[] seed = source.createSeed();
+        final UniformRandomProvider rng1 = source.create(seed);
+        final UniformRandomProvider rng2 = source.create(seed);
+        final int samples = 10;
+        for (int j = 0; j < samples; j++) {
+            ArrayShuffleBenchmark.shuffle1(rng1, a);
+            ArrayShuffleBenchmark.shuffle1(rng2, b);
+            Assertions.assertArrayEquals(a, b);
+        }
+    }
 }

Reply via email to