diff -r 7e791393cc4d -r 1b314be4feb2 src/java.base/share/classes/java/util/random/L64X128MixRandom.java --- a/src/java.base/share/classes/java/util/random/L64X128MixRandom.java Thu Aug 29 11:33:26 2019 -0300 +++ b/src/java.base/share/classes/java/util/random/L64X128MixRandom.java Thu Nov 14 08:54:56 2019 -0400 @@ -28,7 +28,7 @@ import java.math.BigInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.random.RandomGenerator.SplittableGenerator; -import java.util.random.RandomSupport.AbstractSplittableGenerator; +import java.util.random.RandomSupport.AbstractSplittableWithBrineGenerator; /** * A generator of uniform pseudorandom values applicable for use in @@ -54,9 +54,10 @@ * {@link L64X128MixRandom} is a specific member of the LXM family of algorithms * for pseudorandom number generators. Every LXM generator consists of two * subgenerators; one is an LCG (Linear Congruential Generator) and the other is - * an Xorshift generator. Each output of an LXM generator is the sum of one - * output from each subgenerator, possibly processed by a final mixing function - * (and {@link L64X128MixRandom} does use a mixing function). + * an Xorshift generator. Each output of an LXM generator is the result of + * combining state from the LCG with state from the Xorshift generator by + * using a Mixing function (and then the state of the LCG and the state of the + * Xorshift generator are advanced). *

* The LCG subgenerator for {@link L64X128MixRandom} has an update step of the * form {@code s = m * s + a}, where {@code s}, {@code m}, and {@code a} are all @@ -73,7 +74,8 @@ * which can take on any values provided that they are not both zero. * The period of this subgenerator is 2128-1. *

- * The mixing function for {@link L64X128MixRandom} is the 64-bit "starstar(5,7,9)" function. + * The mixing function for {@link L64X128MixRandom} is {@link RandomSupport.mixLea64} + * applied to the argument {@code (s + x0)}. *

* Because the periods 264 and 2128-1 of the two subgenerators * are relatively prime, the period of any single {@link L64X128MixRandom} object @@ -98,8 +100,8 @@ * 2128 subsequence values, nearly all of them (2128-264) * occur 264 times over the course of the entire cycle, and the other * 264 subsequence values occur only 264-1 times. So the ratio - * of the probability of getting one of the less common subsequence values and the - * probability of getting one of the more common subsequence values is 1-2-64. + * of the probability of getting any specific one of the less common subsequence values and the + * probability of getting any specific one of the more common subsequence values is 1-2-64. * (Note that the set of 264 less-common subsequence values will differ from * one instance of {@link L64X128MixRandom} to another, as a function of the additive * parameter of the LCG.) The values produced by the {@code nextInt()}, {@code nextFloat()}, @@ -136,7 +138,7 @@ * * @since 14 */ -public final class L64X128MixRandom extends AbstractSplittableGenerator { +public final class L64X128MixRandom extends AbstractSplittableWithBrineGenerator { /* * Implementation Overview. @@ -179,14 +181,13 @@ BigInteger.ONE.shiftLeft(128).subtract(BigInteger.ONE).shiftLeft(64); /* - * Multiplier used in the LCG portion of the algorithm, taken from - * Pierre L'Ecuyer, Tables of linear congruential generators of - * different sizes and good lattice structure, Mathematics of - * Computation 68, 225 (January 1999), pages 249-260, - * Table 4 (first multiplier for size 264). + * Multiplier used in the LCG portion of the algorithm. + * Chosen based on research by Sebastiano Vigna and Guy Steele (2019). + * The spectral scores for dimensions 2 through 8 for the multiplier 0xd1342543de82ef95 + * are [0.958602, 0.937479, 0.870757, 0.822326, 0.820405, 0.813065, 0.760215]. */ - private static final long M = 2862933555777941757L; + private static final long M = 0xd1342543de82ef95L; /* ---------------- instance fields ---------------- */ @@ -222,9 +223,10 @@ this.x1 = x1; // If x0 and x1 are both zero, we must choose nonzero values. if ((x0 | x1) == 0) { + long v = s; // At least one of the two values generated here will be nonzero. - this.x0 = RandomSupport.mixStafford13(s += RandomSupport.GOLDEN_RATIO_64); - this.x1 = RandomSupport.mixStafford13(s + RandomSupport.GOLDEN_RATIO_64); + this.x0 = RandomSupport.mixStafford13(v += RandomSupport.GOLDEN_RATIO_64); + this.x1 = RandomSupport.mixStafford13(v + RandomSupport.GOLDEN_RATIO_64); } } @@ -245,7 +247,7 @@ // The seed is hashed by mixStafford13 to produce the initial `x0`, // which will then be used to produce the first generated value. // Then x1 is filled in as if by a SplitMix PRNG with - // GOLDEN_RATIO_64 as the gamma value and Stafford13 as the mixer. + // GOLDEN_RATIO_64 as the gamma value and mixStafford13 as the mixer. this(RandomSupport.mixMurmur64(seed ^= RandomSupport.SILVER_RATIO_64), 1, RandomSupport.mixStafford13(seed), @@ -284,26 +286,27 @@ /* ---------------- public methods ---------------- */ /** - * Constructs and returns a new instance of {@link L64X128MixRandom} - * that shares no mutable state with this instance. + * Given 63 bits of "brine", constructs and returns a new instance of + * {@code L64X128MixRandom} that shares no mutable state with this instance. * However, with very high probability, the set of values collectively * generated by the two objects has the same statistical properties as if * same the quantity of values were generated by a single thread using - * a single {@link L64X128MixRandom} object. Either or both of the two + * a single {@code L64X128MixRandom} object. Either or both of the two * objects may be further split using the {@code split} method, * and the same expected statistical properties apply to the * entire set of generators constructed by such recursive splitting. * - * @param source a {@link SplittableGenerator} instance to be used instead + * @param source a {@code SplittableGenerator} instance to be used instead * of this one as a source of pseudorandom bits used to * initialize the state of the new ones. - * - * @return a new instance of {@link L64X128MixRandom} + * @param brine a long value, of which the low 63 bits are used to choose + * the {@code a} parameter for the new instance. + * @return a new instance of {@code L64X128MixRandom} */ - public L64X128MixRandom split(SplittableGenerator source) { - // Literally pick a new instance "at random". - return new L64X128MixRandom(source.nextLong(), source.nextLong(), - source.nextLong(), source.nextLong()); + public SplittableGenerator split(SplittableGenerator source, long brine) { + // Pick a new instance "at random", but use the brine for `a`. + return new L64X128MixRandom(brine << 1, source.nextLong(), + source.nextLong(), source.nextLong()); } /** @@ -312,8 +315,12 @@ * @return a pseudorandom {@code long} value */ public long nextLong() { - final long z = s + x0; - s = M * s + a; // LCG + // Compute the result based on current state information + // (this allows the computation to be overlapped with state update). + final long result = RandomSupport.mixLea64(s + x0); + // Update the LCG subgenerator + s = M * s + a; + // Update the Xorshift subgenerator long q0 = x0, q1 = x1; { // xoroshiro128v1_0 q1 ^= q0; @@ -322,9 +329,15 @@ q1 = Long.rotateLeft(q1, 37); } x0 = q0; x1 = q1; - return Long.rotateLeft(z * 5, 7) * 9; // "starstar" mixing function + return result; } + /** + * Returns the period of this random generator. + * + * @return a {@link BigInteger} whose value is the number of distinct possible states of this + * {@link RandomGenerator} object (264(2128-1)). + */ public BigInteger period() { return PERIOD; }