New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
speed LCG up 10x (Safari) or 4x (Chrome/Firefox) #36
Conversation
A performance comparison table: The fastest with Safari is your ca0a54d ; but I get better performance under Chrome by forcing it to use a Float32 with a (1-value) typed array. |
In Chrome on my machine the UInt32Array version is slower. If you want to reduce code size, using lcgG = {
const mul = 0x19660D, inc = 0x3C6EF35F, eps = 1/0x100000000;
return function lcg(seed = Math.random()) {
if (0 >= seed || seed >= 1) throw new RangeError("invalid seed");
let state = seed/eps |0;
return () => (state = mul * state + inc |0, eps * (state >>>0));
};
} |
if (0 >= seed || seed >= 1) throw new RangeError("invalid seed"); This won’t catch NaN, which is the reason for the awkward |
I just copy/pasted that bit from @Fil’s notebook. Entirely fine to leave it as it was. There are also a lot of other possible variations: using other type of input as a seed; outputting random 32-bit integers; outputting floats with more than 32 bits of randomness; making a slightly more expensive prng that is more statistically random; etc. |
see #35 (comment)
would it be enough to remultiply by 2^32?
Is it as simple as taking two successive numbers a,b and return a + 2^32 * b? I think for the former 3 ideas, we'd rather keep the library minimal.
the work on pcg is also very exciting! PS: One thing I'd like to do, now that we're deep in this rabbit hole, is run actual rng test suites on our generators. I've tried doing the following to output a test file (or pipe), but node crashes after 40Mb. const mul = 0x19660D;
const inc = 0x3C6EF35F;
const eps = 1/0x100000000;
function int32lcg(seed = Math.random()) {
if (!(0 <= seed && seed < 1)) throw new RangeError("invalid seed");
let state = seed/eps | 0;
return () => (state = mul * state + inc | 0, (state & 0xffffffff));
}
const buff = Buffer.allocUnsafe(4);
const l = int32lcg(0.5);
do {
buff.writeInt32LE(l());
process.stdout.write(buff);
} while (true); The test would be:
|
It's really fun to test variants that you think would do less operations but end up being slower. For example lcgI = {
const mul = 0x19660D,
inc = 0x3C6EF35F,
eps = 1 / 0x100000000;
return function lcg(seed = Math.random()) {
if (!(0 <= seed && seed < 1)) throw new RangeError("invalid seed");
let state = (seed / eps) | 0;
return () => eps * (state = (mul * state + inc) >>> 0);
};
} is about 10% faster than lcgG in Chrome (which I expected since it does one Anyway… |
Co-authored-by: Mike Bostock <mbostock@gmail.com>
See https://observablehq.com/d/7723fc9ac405bb8e