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 Uint32Array
s, 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.