From bb0bdd26b342e0227995d47a48dfb1c6c55ffefa Mon Sep 17 00:00:00 2001
From: stepan <stepan.sindelar@oracle.com>
Date: Tue, 29 Nov 2016 14:36:06 +0100
Subject: [PATCH] RRNG: save some allocations when handling .Random.seed

---
 .../truffle/r/runtime/rng/RNGInitAdapter.java | 23 +++++++++--
 .../oracle/truffle/r/runtime/rng/RRNG.java    | 31 +++++---------
 .../r/runtime/rng/RandomNumberGenerator.java  |  7 +++-
 .../r/runtime/rng/mm/MarsagliaMulticarry.java | 23 +++++------
 .../r/runtime/rng/mt/MersenneTwister.java     | 40 +++++--------------
 .../truffle/r/runtime/rng/user/UserRNG.java   | 15 +++++--
 6 files changed, 66 insertions(+), 73 deletions(-)

diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RNGInitAdapter.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RNGInitAdapter.java
index e0eeb82721..a7f110a81d 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RNGInitAdapter.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RNGInitAdapter.java
@@ -11,21 +11,36 @@
  */
 package com.oracle.truffle.r.runtime.rng;
 
+/**
+ * Manages the iSeed array for generators and contains some useful common code.
+ */
 public abstract class RNGInitAdapter implements RandomNumberGenerator {
 
     protected static final double I2_32M1 = 2.3283064365386963e-10;
+    protected static final int MAX_ISEED_SIZE = 625;
 
     // TODO: it seems like GNU R this is shared between the generators (does it matter?)
-    protected final int[] iSeed = new int[625];
+    private int[] iSeed = new int[MAX_ISEED_SIZE + 1];
 
     @Override
     public void setISeed(int[] seeds) {
-        for (int i = 1; i <= getNSeed(); i++) {
-            iSeed[i - 1] = seeds[i];
-        }
+        iSeed = seeds;
         fixupSeeds(false);
     }
 
+    @Override
+    public int[] getSeeds() {
+        return iSeed;
+    }
+
+    protected int getISeedItem(int index) {
+        return iSeed[index + 1];
+    }
+
+    protected void setISeedItem(int index, int value) {
+        iSeed[index + 1] = value;
+    }
+
     /**
      * Ensure 0 and 1 are never returned from rand generation algorithm.
      */
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RRNG.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RRNG.java
index 3509333bad..3310329048 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RRNG.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RRNG.java
@@ -41,6 +41,9 @@ import com.oracle.truffle.r.runtime.rng.user.UserRNG;
  * uncontrolled way, which then has to be checked. Currently we do not support reading it, although
  * we do create/update it when the seed/kind is changed, primarily as a debugging aid. N.B. GnuR
  * updates it on <i>every</i> random number generation!
+ *
+ * Important note: make sure to invoke {@link #getRNGState()} before invoking any other methods from
+ * this class and to invoke {@link #putRNGState()} when done witch random number generation.
  */
 public class RRNG {
     /**
@@ -381,6 +384,10 @@ public class RRNG {
             } else if (seedsObj instanceof RIntVector) {
                 RIntVector seedsVec = (RIntVector) seedsObj;
                 seeds = seedsVec.getDataWithoutCopying();
+                if (seeds == currentGenerator().getSeeds()) {
+                    // no change of the .Random.seed variable
+                    return;
+                }
             } else {
                 // seedsObj is not valid, which should have been reported and fixed in getRNGKind
                 return;
@@ -404,26 +411,8 @@ public class RRNG {
     @TruffleBoundary
     public static void putRNGState() {
         int[] seeds = currentGenerator().getSeeds();
-        int lenSeeds = currentGenerator().getNSeed();
-
-        // we update the existing vector from global env if possible
-        int[] data;
-        Object prevState = getDotRandomSeed();
-        boolean canReusePrev = prevState instanceof RIntVector && ((RIntVector) prevState).getLength() == lenSeeds + 1 && !((RIntVector) prevState).isShared();
-        if (canReusePrev) {
-            data = ((RIntVector) prevState).getDataWithoutCopying();
-        } else {
-            data = new int[lenSeeds + 1];
-        }
-
-        data[0] = currentKind().ordinal() + 100 * currentNormKind().ordinal();
-        for (int i = 0; i < lenSeeds; i++) {
-            data[i + 1] = seeds[i];
-        }
-
-        if (!canReusePrev) {
-            RIntVector vector = RDataFactory.createIntVector(data, RDataFactory.COMPLETE_VECTOR);
-            REnvironment.globalEnv().safePut(RANDOM_SEED, vector);
-        }
+        seeds[0] = currentKind().ordinal() + 100 * currentNormKind().ordinal();
+        RIntVector vector = RDataFactory.createIntVector(seeds, RDataFactory.COMPLETE_VECTOR);
+        REnvironment.globalEnv().safePut(RANDOM_SEED, vector.makeSharedPermanent());
     }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RandomNumberGenerator.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RandomNumberGenerator.java
index 3ca1a0f164..8be86f72a0 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RandomNumberGenerator.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RandomNumberGenerator.java
@@ -21,6 +21,10 @@ public interface RandomNumberGenerator {
 
     void fixupSeeds(boolean initial);
 
+    /**
+     * Returns array in the format of .Random.seed, i.e. under index 0 is id of the generator and
+     * the rest are the actual seeds.
+     */
     int[] getSeeds();
 
     double genrandDouble();
@@ -34,7 +38,8 @@ public interface RandomNumberGenerator {
 
     /**
      * Sets array which contains current generator flag under index 0 and seeds for random
-     * generation under the other indices.
+     * generation under the other indices. The generator may use the array as is without making a
+     * defensive copy, the caller must make sure the array contents do not get changed.
      */
     void setISeed(int[] seeds);
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/mm/MarsagliaMulticarry.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/mm/MarsagliaMulticarry.java
index 697e3c65ac..34a99d4b2d 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/mm/MarsagliaMulticarry.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/mm/MarsagliaMulticarry.java
@@ -26,7 +26,7 @@ public final class MarsagliaMulticarry extends RNGInitAdapter {
         int seed = seedParam;
         for (int i = 0; i < getNSeed(); i++) {
             seed = (69069 * seed + 1);
-            iSeed[i] = seed;
+            setISeedItem(i, seed);
         }
         fixupSeeds(true);
     }
@@ -34,29 +34,24 @@ public final class MarsagliaMulticarry extends RNGInitAdapter {
     @Override
     @TruffleBoundary
     public void fixupSeeds(boolean initial) {
-        if (iSeed[0] == 0) {
-            iSeed[0] = 1;
+        if (getISeedItem(0) == 0) {
+            setISeedItem(0, 1);
         }
-        if (iSeed[1] == 0) {
-            iSeed[1] = 1;
+        if (getISeedItem(1) == 0) {
+            setISeedItem(1, 1);
         }
     }
 
-    @Override
-    public int[] getSeeds() {
-        return iSeed;
-    }
-
     @Override
     public double genrandDouble() {
-        int state0 = iSeed[0];
-        int state1 = iSeed[1];
+        int state0 = getISeedItem(0);
+        int state1 = getISeedItem(1);
         state0 = 36969 * (state0 & 0177777) + (state0 >>> 16);
         state1 = 18000 * (state1 & 0177777) + (state1 >>> 16);
         int x = (state0 << 16) ^ (state1 & 0177777);
         double d = (x & 0xffffffffL) * I2_32M1;
-        iSeed[0] = state0;
-        iSeed[1] = state1;
+        setISeedItem(0, state0);
+        setISeedItem(1, state1);
         return fixup(d); /* in [0,1) */
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/mt/MersenneTwister.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/mt/MersenneTwister.java
index b084498c04..d9472bda68 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/mt/MersenneTwister.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/mt/MersenneTwister.java
@@ -95,36 +95,18 @@ public final class MersenneTwister extends RNGInitAdapter {
      * pointer arithmetic to set {@code mt} to {@code dummy + 1}.
      */
     private int getMt(int i) {
-        return iSeed[i + 1];
+        return getISeedItem(i + 1);
     }
 
     private void setMt(int i, int val) {
-        iSeed[i + 1] = val;
+        setISeedItem(i + 1, val);
     }
 
-    // to keep variable naming (somewhat) consistent with GNU R
-    private int[] dummy = iSeed;
-
     @Override
     public void setISeed(int[] seeds) {
-        boolean changed = false;
-        for (int i = 1; i <= getNSeed(); i++) {
-            changed |= iSeed[i - 1] != seeds[i];
-            iSeed[i - 1] = seeds[i];
-        }
         fixupSeeds(false);
-        // kill the current buffer if seed changes
-        if (changed) {
-            bufferIndex = BUFFER_SIZE;
-        }
-    }
-
-    /**
-     * We have to recreate the effect of having the number of seeds in the array.
-     */
-    @Override
-    public int[] getSeeds() {
-        return iSeed;
+        // kill the current buffer if the seed changes
+        bufferIndex = BUFFER_SIZE;
     }
 
     /**
@@ -141,7 +123,7 @@ public final class MersenneTwister extends RNGInitAdapter {
         int seed = seedParam;
         for (int i = 0; i < getNSeed(); i++) {
             seed = (69069 * seed + 1);
-            iSeed[i] = seed;
+            setISeedItem(i, seed);
         }
         fixupSeeds(true);
         bufferIndex = BUFFER_SIZE;
@@ -151,14 +133,14 @@ public final class MersenneTwister extends RNGInitAdapter {
     @TruffleBoundary
     public void fixupSeeds(boolean initial) {
         if (initial) {
-            iSeed[0] = N;
+            setISeedItem(0, N);
         }
-        if (iSeed[0] <= 0) {
-            iSeed[0] = N;
+        if (getISeedItem(0) <= 0) {
+            setISeedItem(0, N);
         }
         boolean notAllZero = false;
         for (int i = 1; i <= N; i++) {
-            if (iSeed[i] != 0) {
+            if (getISeedItem(i) != 0) {
                 notAllZero = true;
             }
         }
@@ -173,7 +155,7 @@ public final class MersenneTwister extends RNGInitAdapter {
     @Override
     public double genrandDouble() {
         if (bufferIndex == BUFFER_SIZE) {
-            int localDummy0 = dummy[0];
+            int localDummy0 = getISeedItem(0);
             int localMti = localDummy0;
             // It appears that this never happens
             // sgenrand(4357);
@@ -216,7 +198,7 @@ public final class MersenneTwister extends RNGInitAdapter {
                 localMti = 0;
             }
             localDummy0 = localMti;
-            dummy[0] = localDummy0;
+            setISeedItem(0, localDummy0);
             bufferIndex = 0;
         }
         return buffer[bufferIndex++];
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/user/UserRNG.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/user/UserRNG.java
index 79a8117b3b..84187071f9 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/user/UserRNG.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/user/UserRNG.java
@@ -28,13 +28,13 @@ import com.oracle.truffle.r.runtime.ffi.DLL;
 import com.oracle.truffle.r.runtime.ffi.DLL.DLLInfo;
 import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 import com.oracle.truffle.r.runtime.ffi.UserRngRFFI;
-import com.oracle.truffle.r.runtime.rng.RNGInitAdapter;
 import com.oracle.truffle.r.runtime.rng.RRNG.Kind;
+import com.oracle.truffle.r.runtime.rng.RandomNumberGenerator;
 
 /**
  * Interface to a user-supplied RNG.
  */
-public final class UserRNG extends RNGInitAdapter {
+public final class UserRNG implements RandomNumberGenerator {
     private static final boolean OPTIONAL = true;
 
     public enum Function {
@@ -124,8 +124,10 @@ public final class UserRNG extends RNGInitAdapter {
         if (!Function.Seedloc.isDefined()) {
             return null;
         }
-        int[] result = new int[nSeeds];
-        userRngRFFI.seeds(result);
+        int[] seeds = new int[nSeeds];
+        userRngRFFI.seeds(seeds);
+        int[] result = new int[nSeeds + 1];
+        System.arraycopy(seeds, 0, result, 1, seeds.length);
         return result;
     }
 
@@ -144,4 +146,9 @@ public final class UserRNG extends RNGInitAdapter {
         return nSeeds;
     }
 
+    @Override
+    public void setISeed(int[] seeds) {
+        // TODO: userRNG seems to be not using iseed?
+    }
+
 }
-- 
GitLab