More PractRand Passes: ChaCha & Truncated XorShift*
Continuing my recent burst of PRNG testing with PractRand, here are a few more passes, this time for the generators I mentioned in my post on reasonable alternatives to PCG.
ChaCha<3>
ChaCha<4>
 XorShift* 64/32 (i.e., high 32 bits of 64bit XorShift*)
 XorShift* 128/64 (i.e., high 64 bits of 128bit XorShift*)
None of these results should be a surprise. In the PCG paper, I looked at truncated XorShift* generators and onserved that they passed TestU01 with plenty of headroom. Nevetheless, it's nice to confirm.
Also, none of these generators are trivially predictable (but obviously the smaller XorShift versions can be fairly quickly brute forced). ChaCha obviously has much stronger prediction difficulty, given that with more rounds it is considered cryptographically secure. When simply trying to protect against algorithmic complexity attacks, I think four rounds is fine.
Testing ChaCha
I used Orson Peters's implementation.
ChaCha<1>
and ChaCha<2>
Without enough rounds, ChaCha fails statistical tests badly. Don't use these ones.
ChaCha<3>
^{1}
ChaCha<3>
linux$ ./chacha.3  ./RNG_test.new stdin32
ChaCha<3>(0x0daf885421137063,0xf476e1c71263aa18) initialized.
RNG_test using PractRand version 0.93
RNG = RNG_stdin32, seed = 0x33ed287e
test set = normal, folding = standard (32 bit)
rng=RNG_stdin32, seed=0x33ed287e
length= 128 megabytes (2^27 bytes), time= 2.8 seconds
no anomalies in 117 test result(s)
[...]
rng=RNG_stdin32, seed=0x33ed287e
length= 8 terabytes (2^43 bytes), time= 166331 seconds
no anomalies in 244 test result(s)
rng=RNG_stdin32, seed=0x33ed287e
length= 16 terabytes (2^44 bytes), time= 340113 seconds
no anomalies in 252 test result(s)
rng=RNG_stdin32, seed=0x33ed287e
length= 32 terabytes (2^45 bytes), time= 684283 seconds
no anomalies in 260 test result(s)
Looks good all the way out to 32 TB!
ChaCha<4>
linux$ ./chacha.4  ./RNG_test stdin32
ChaCha<4>(0x422327c44b169678,0xe785ae07c0439891) initialized.
RNG_test using PractRand version 0.93
RNG = RNG_stdin32, seed = 0xc9e6f300
test set = normal, folding = standard (32 bit)
rng=RNG_stdin32, seed=0xc9e6f300
length= 128 megabytes (2^27 bytes), time= 2.4 seconds
no anomalies in 117 test result(s)
rng=RNG_stdin32, seed=0xc9e6f300
length= 256 megabytes (2^28 bytes), time= 5.3 seconds
no anomalies in 124 test result(s)
[...]
rng=RNG_stdin32, seed=0xc9e6f300
length= 8 terabytes (2^43 bytes), time= 166219 seconds
no anomalies in 244 test result(s)
rng=RNG_stdin32, seed=0xc9e6f300
length= 16 terabytes (2^44 bytes), time= 332149 seconds
no anomalies in 252 test result(s)
rng=RNG_stdin32, seed=0xc9e6f300
length= 32 terabytes (2^45 bytes), time= 664913 seconds
no anomalies in 260 test result(s)
Also looks good (unsurprising since .ChaCha<3>
passed)
Truncated XorShift*
XorShift* generators are widely known, and back in 2014, I considered them the main competition for PCG. Other implementations exist, no doubt, but for fun I wrote my own; you can find my C++ implementation as a Github gist here.
Since I was doing the math to find suitable shift parameters and multipliers, I figured “why do one when you can do four”, so the code provides for distinct (and hopefully statistically independent) generators at each size. Of course that gives more to test.
XorShift* 64/32
I'm starting to move to mutltithreaded testing to get things done faster.
linux$ ./xorshift64star32a  ./RNG_test stdin32 multithreaded
xorshift64star32a(0xee472e19cf5b5c8c) initialized.
RNG_test using PractRand version 0.93
RNG = RNG_stdin32, seed = 0x1a9bd1f6
test set = normal, folding = standard (32 bit)
rng=RNG_stdin32, seed=0x1a9bd1f6
length= 256 megabytes (2^28 bytes), time= 2.3 seconds
no anomalies in 124 test result(s)
rng=RNG_stdin32, seed=0x1a9bd1f6
length= 512 megabytes (2^29 bytes), time= 4.8 seconds
no anomalies in 132 test result(s)
[...]
rng=RNG_stdin32, seed=0x1a9bd1f6
length= 8 terabytes (2^43 bytes), time= 53722 seconds
no anomalies in 244 test result(s)
rng=RNG_stdin32, seed=0x1a9bd1f6
length= 16 terabytes (2^44 bytes), time= 107537 seconds
no anomalies in 252 test result(s)
rng=RNG_stdin32, seed=0x1a9bd1f6
length= 32 terabytes (2^45 bytes), time= 215209 seconds
no anomalies in 260 test result(s)
Passes all the way out to 32 TB. So do the other three versions.
XorShift* 128/64
The tests for these four are only up to 16 TB at this point, but since
their smaller siblings did great, I don't see any likelihood of
problems. Here are the results so far for xorshift128star64a
:
$linux ./xorshift128star64a  time ./RNG_test stdin64 multithreaded
xorshift128star64a(0xeae02e72a9af2d09) initialized.
RNG_test using PractRand version 0.93
RNG = RNG_stdin64, seed = 0xc7577148
test set = normal, folding = standard (64 bit)
rng=RNG_stdin64, seed=0xc7577148
length= 256 megabytes (2^28 bytes), time= 2.2 seconds
Test Name Raw Processed Evaluation
[Low4/64]BCFN(2+1,135,T) R= 6.2 p =18.9e4 unusual
...and 158 test result(s) without anomalies
rng=RNG_stdin64, seed=0xc7577148
length= 512 megabytes (2^29 bytes), time= 4.8 seconds
no anomalies in 169 test result(s)
rng=RNG_stdin64, seed=0xc7577148
length= 1 gigabyte (2^30 bytes), time= 9.3 seconds
no anomalies in 180 test result(s)
[...]
rng=RNG_stdin64, seed=0xc7577148
length= 4 terabytes (2^42 bytes), time= 27422 seconds
no anomalies in 308 test result(s)
rng=RNG_stdin64, seed=0xc7577148
length= 8 terabytes (2^43 bytes), time= 54853 seconds
no anomalies in 319 test result(s)
rng=RNG_stdin64, seed=0xc7577148
length= 16 terabytes (2^44 bytes), time= 109397 seconds
no anomalies in 329 test result(s)
I'll update this post when the test finishes.

2018 update: In a personal communication, Daniel Watson noted that in his experience ChaCha with three rounds quickly fails statistical tests. It turns out that in Orson Peters's implementation, specifying an odd number rounds actually performs the next even number of rounds, so
ChaCha<3>
is reallyChaCha<4>
. I have since adapted his implementation to create my own variant which properly supports an odd number of rounds. With that corrected implementation,ChaCha<3>
fails. ↩