Billions of Random Numbers in a Blink of an Eye
dragan.rocks
dragan.rocks
Deep Learning for Programmers at https://aiprobook.com/deep-learning-for-programmers/
Numerical Linear Algebra for Programmers at https://aiprobook.com/numerical-linear-algebra-for-programme...
Everything uses open source software:
Once you've got it seeded, you can get random numbers from your system's platform CSPRNG plenty fast, and CSPRNGs make, for most applications, a reasonable default RNG.
2. The magic ingredients of a CSPRNG are not only the "secret entropy" for seeding, but also the property that given a sequence of its outputs, it's sufficiently hard to predict the next output (this implies that it won't fail the usual statistical tests - every CSPRNG is a PRNG, but not vice versa: for example, with 624 outputs of the Mersenne Twister, you can predict all subsequent ones.)
This next-bit test property was sort of implied (by saying "stream cipher"), but I wanted to spell it out, as it's good to have in one's mental model.
3. For some purposes (such as Monte Carlo simulations), a non-CSPRNG makes sense, as they're still faster. (But given the asymmetric payoffs of a mistake, default should be CSPRNG.)
And what everyone needs to know is that it doesn't hold for many (non-CS) PRNG, as apparently poker site operators have found out to their chagrin.
[1] https://en.wikipedia.org/wiki/Low-discrepancy_sequence [2] https://blog.demofox.org/2017/05/29/when-random-numbers-are-...
Nowadays with highly optimized and often hardware-accelerated crypto, I honestly doubt it matters.
Using "openssl rand", I can generate randomness 2x as fast as with a naive
while (1) { r = rand(); fwrite(&r, 4, 1, stdout); }
compiled with -O2. If I replace "openssl rand" with "openssl enc -aes-128-ctr < /dev/zero", I get randomness 4 times faster again, i.e. 8 times as fast as with the naive approach using rand(), hundreds of megabytes on a single core.And I don't have to worry about the properties of the PRNG.
[1]: http://michaelbrundage.com/note/2005/01/05/random-number-gen...
https://www.deshawresearch.com/resources_random123.html
http://www.thesalmons.org/john/random123/releases/1.00/docs/...
http://www.thesalmons.org/john/random123/papers/random123sc1...
https://neanderthal.uncomplicate.org/
I saw first: "implemented in Clojure" and didn't understand how it's "100x faster than optimized pure Java".
But reading further it's clear:
"Does not try to poorly reinvent the wheel. Respects decades of BLAS and LAPACK standardization. Does the number crunching part with native BLAS and/or GPU, but provides a tiny Clojure layer for sanity. Win-win :)"
1 Billion 32 bit numbers in 347 ms is 23 G Bytes/s. In 40ms is 200 G Bytes/s. DDR4 is 26 G Bytes per second. And Nvidia GPUs do indeed have over 200 G Bytes/s: https://www.microway.com/knowledge-center-articles/compariso...
In [3]: %timeit np.random.normal(1000000000)
1.3 µs ± 26.2 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
Edit: and 2.5e17 in ~7.95 microseconds
In [5]: %timeit np.random.normal((500000000, 500000000))
7.95 µs ± 90.7 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
I get results about 100x slower those of the article's CPU benchmark when using numpy on a 2019 MBP:
python -m timeit -s 'import numpy as np; x = np.zeros(1000000)' -u msec 'np.random.normal(x)'
10 loops, best of 5: 30.9 msec per loop
edit: I'm no python wizard myself, so I'm perfectly willing to believe that there's a better way to generate a random array in numpy than what I'm doing.You might want to try this one:
np.random.normal(size=1000000000)
For size=1000 I get on my machine about 36 ns per number (total 36 µsecs). You probably measured for size=1 but a normal of 1 billion, not 1 billion numbers at a normal of 1.For any conceivable application, you just need 256 bits of entropy and a good CSPRNG. https://blog.cr.yp.to/20170723-random.html