class SplittableRandom
Guy Steele
guy.steele at oracle.com
Tue Jul 16 18:57:24 UTC 2013
On Jul 15, 2013, at 4:13 PM, Martin Buchholz <martinrb at google.com> wrote:
>
> On Mon, Jul 15, 2013 at 9:59 AM, Martin Buchholz <martinrb at google.com>wrote:
>>
>> Also, if gamma is much less than 2^64 (say 2^56; that number of gammas
>> "should be enough for anybody"), then the above will be a little more
>> efficient since the wraparound code will be rare and well-predicted. The
>> bits that become available as a result can then be optionally donated to
>> the seed space!
I just looked into this. It's not a good idea to generate 64-bit gamma values and
then filter them; it's too costly to do rejection sampling, and if you just throw away
bits, you risk getting duplicate gamma values, which is undesirable.
So instead I took the latest version and modified the generation of gamma values
to use a 56-bit GAMMA_GAMMA value and perform arithmetic modulo 2^56-5
(I will call this prime "Arthur"). I also threw together a 56-bit mixing function "mix56"
by simply adding a little bit of masking to mix64; I emphasize, however, that I have not
yet performed avalanche testing on mix56. The point of mix56 is that it is (I think)
a bijective function of 56-bit values, whereas applying mix64 and then discarding
8 bits would not be. Therefore all the generated gamma values should be different.
private static final long GAMMA_PRIME = (1L << 56) - 5; // Arthur, a prime number
private static final long GAMMA_GAMMA = 0x00281E2DBA6606F3L; // must be less than Arthur
private GreatestRandom(long seed, long splitSeed) {
this.seed = seed;
long s = (splitSeed & 0x7FFFFFFFFFFFFFFFL) % GAMMA_PRIME, g;
do { // ensure gamma >= 13, considered as an unsigned integer
s += GAMMA_GAMMA;
if (s >= GAMMA_PRIME) s -= GAMMA_PRIME;
g = mix56(s);
} while (g >= 0L && g < 13L);
this.gamma = g;
this.nextSplit = s;
}
private static long mix56(long z) {
z ^= (z >>> 33);
z *= 0xff51afd7ed558ccdL;
z &= 0x00FFFFFFFFFFFFFFL;
z ^= (z >>> 33);
z *= 0xc4ceb9fe1a85ec53L;
z &= 0x00FFFFFFFFFFFFFFL;
z ^= (z >>> 33);
return z;
}
So here's the punchline: this change makes my Monte Carlo calculation of pi run over 19% faster!
Hard to believe. It's certainly avoiding most of the hard work in addGammaModGeorge,
and perhaps also making branch prediction totally win, but 19% ??? Someone please verify this
independently.
Still to be done: (a) verify avalanche behavior of mix56; (b) run more DieHarder tests.
--Guy
More information about the core-libs-dev
mailing list