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