Skip to content
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

Merged
merged 3 commits into from Aug 26, 2020
Merged

Conversation

jrus
Copy link
Contributor

@jrus jrus commented Aug 22, 2020

@jrus jrus changed the title speed up 10x (Safari) or 4x (Chrome/Firefox) speed LCG up 10x (Safari) or 4x (Chrome/Firefox) Aug 22, 2020
@Fil
Copy link
Member

Fil commented Aug 22, 2020

A performance comparison table:
https://observablehq.com/d/b1aa7c52fc50a054

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.

@jrus
Copy link
Contributor Author

jrus commented Aug 22, 2020

In Chrome on my machine the UInt32Array version is slower.

If you want to reduce code size, using | 0 seems just as fast as & 0xFFFFFFFF. This version is fastest or tied for fastest in every browser here (beware of variability between runs; the benchmarking code there isn’t super robust):

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));
  };
}

@mbostock
Copy link
Member

mbostock commented Aug 22, 2020

if (0 >= seed || seed >= 1) throw new RangeError("invalid seed");

This won’t catch NaN, which is the reason for the awkward if (!(…)). But otherwise that last version looks good.

@jrus
Copy link
Contributor Author

jrus commented Aug 23, 2020

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.

@Fil
Copy link
Member

Fil commented Aug 23, 2020

using other type of input as a seed;

see #35 (comment)

outputting random 32-bit integers;

would it be enough to remultiply by 2^32?

outputting floats with more than 32 bits of randomness;

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.

a slightly more expensive prng that is more statistically random; etc.

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.
https://www.pcg-random.org/posts/how-to-test-with-testu01.html

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:

node lcg.js | ../PractRand/RNG_test stdin

@Fil
Copy link
Member

Fil commented Aug 25, 2020

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 |0 less), but is 3 times slower in Safari. I don't have the beginning of an intuition for what is happening.

Anyway…

src/lcg.js Outdated Show resolved Hide resolved
Co-authored-by: Mike Bostock <mbostock@gmail.com>
@Fil Fil merged commit 97c3c8a into d3:master Aug 26, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants