benchmarking JS random number generation

What’s the fastest way to fill a Float32Array with random numbers?


The problem is as follows - I have a Float32Array and I want to fill it up with random numbers. This array might be large! What’s the fastest way to do this?

There’s a few things to note - the actual values don’t have to satisfy any real properties for my usecase. It just has to be seemingly random, and the range of numbers generated doesn’t really matter.

Benchmarking

For benchmarking we’ll use the following function:

function benchmark(f, x) {
  const start = performance.now();
  f(x);
  const end = performance.now();
  return end - start;
}

I’ll be including the results on my laptop, but will also provide a button for you to run the benchmark yourself!

Using Math.random

This is the naive approach. We can do the following:

function usingMathRandom(len) {
  const arr = new Float32Array(len);
  for (let i = 0; i < len; i++) {
    arr[i] = Math.random();
  }
  return arr;
}


Using Crypto.getRandomValues

Crypto.getRandomValues takes in a buffer of length up to 65536 and populates the whole buffer with random values. It only operates on Uint32Arrays, but we can share the underlying buffer between a Float32Array and a Uint32Array.

function usingCryptoRandom(x) {
  const arr  = new Float32Array(x);
  for (let i = 0; i < x * 4; i += 65536) {
    const arr_u = new Uint32Array(arr.buffer.slice(i, i + 65536));
    crypto.getRandomValues(arr_u);
  }
  return arr;
}


Results (time in ms)

Firefox

Array size Math.random crypto.getRandomValues
10^1 1 0
10^2 0 0
10^3 0 0
10^4 1 0
10^5 0 2
10^6 2 66
10^7 31 103
10^8 303 1098

Chromium

Array size Math.random crypto.getRandomValues
10^1 0 0
10^2 0 0
10^3 0 0
10^4 0 0
10^5 3 0
10^6 12 8
10^7 113 100
10^8 1139 917

Conclusion

I was mildly surprised to see that Math.random on ff is so fast. I suppose that makes sense as it does not need to satisfy any cryptographically secure properties, but the fact that crypto.getRandomValues took a buffer made me wonder if it was more optimized for this usecase. It’s interesting that on chromium, Crypto.getRandomValues is faster than Math.random, so maybe the right choice here depends on the browser. Either way, I’ll be sticking to Math.random since it’s simpler.

Written on September 1, 2022