tandem rng tandem-r GitHub

Performance

pixi run bench produces the figures.

CPU

Apple M4, one thread, pixi run bench, 2^22 draws, minimum of five runs, GiB/s of output, the median of three passes in one session. Doubles count 8 bytes, integers 4. The Tandem column calls the package's fills. The other two call base R with Tandem as the user-supplied generator and with Mersenne-Twister. rnorm uses Ahrens-Dieter, the fastest normal.kind on Mersenne-Twister: inversion, the default, writes 0.48 GiB/s.

Tandem user-supplied Tandem Mersenne-Twister
uniform, tandem_runif and runif 7.7 1.9 1.8
normal, tandem_rnorm and rnorm 4.8 0.85 0.74
exponential, tandem_rexp and rexp 4.2 0.45 0.35
tandem_sample_int(rng, n, 1000) and sample.int(1000, n, TRUE) 6.7 0.57 0.60
the same, weighted by 1:1000 0.79 0.18 0.18

The Tandem fills are the C fill plus R's allocation of the result. The user-supplied hook returns one double per call, so runif through it runs at R's call rate. The hook fills a buffer of 1024 doubles at a time and keeps its state in .Random.seed. A draw checks one 64-bit token against the buffer's. The cold paths, a rebuild and the move to the next buffer, are kept out of line. pixi run bench installs the package first, so it measures the current sources.