4 min read
How fast can you mint random bytes — secrets.token_bytes, os.urandom, or random.getrandbits → bytes? This lab reports MB/s (and ops/s for small sizes) on Linux localhost.
It is not a remake of the uuid4 vs secrets token localhost lab (UUID/token formatting ops/s). Here the focus is bulk byte generation and the crypto vs non-crypto fork: use secrets / os.urandom for security-sensitive material; use random only for simulations / fuzz / non-secret noise.
Related links:
Lab honesty (1 Oct 2026 IST): Python 3.13.5. Affiliates: 0. No Docker. Not a cryptographic proof — throughput + clear API guidance only.
Verdict up front (1 MiB): secrets.token_bytes ~552.7 MB/s ≈ os.urandom ~551.9 MB/s; chunked getrandbits ~48.5 MB/s (~11.39× slower). At 16 B tokens, urandom ~3.32 Mops/s beat secrets ~1.46 Mops/s — still use secrets for API clarity on security paths.
| Arm | Role |
|---|---|
secrets.token_bytes(n) | CSPRNG wrapper — preferred for secrets |
os.urandom(n) | same OS entropy; lower-level |
random.getrandbits chunked/oneshot | MT / non-crypto — simulations only |
Script: lab-evidence/88-secrets-vs-urandom/results/run_lab.py.
| Size | secrets | os.urandom | getrandbits chunked |
|---|---|---|---|
| 1 KiB | 451.7 | 461.7 | 63.0 |
| 64 KiB | 546.3 | 552.8 | 62.7 |
| 1 MiB | 552.7 | 551.9 | 48.5 |
Oneshot getrandbits(n*8).to_bytes(...) at 64 KiB hit ~492.3 MB/s — closer to CSPRNG speed, still not suitable for secrets.
| Size | secrets | os.urandom | getrandbits oneshot |
|---|---|---|---|
| 16 B | 1461976 | 3322269 | 2617834 |
| 32 B | 1550389 | 2008020 | 2493766 |
secrets ≈ os.urandom for bulk on this host (~550 MB/s MiB) — secrets is the right default API for passwords/tokens/keys.random.getrandbits is for games/sim, and naive 8-byte chunked packing was ~11.39× slower than CSPRNG here — oneshot closes the gap but does not become cryptographically safe.random because a microbench looked hot at 16 B.| Need | Use |
|---|---|
| Session IDs, API keys, CSRF, password salts | secrets (or os.urandom) |
| Shuffle a playlist / Monte Carlo | random |
| “Looks random” test fixtures | either; prefer secrets if fixtures ever leak into prod auth |
Lab 41 compared uuid4 vs secrets.token_hex / token_urlsafe mint rates. This lab streams raw bytes across sizes and includes random.getrandbits, with an explicit non-crypto warning.
At 16–32 B the metric that matters is ops/s (auth cookies, CSRF). At ≥1 KiB, MB/s matters (key material, padding, test payloads). On this box both CSPRNG APIs converge near ~550 MB/s once buffers are large; the interesting fork is whether random is allowed at all — for secrets, no.
random for tokens because it is “fast enough”.getrandbits calls (chunked tax).secrets in security code for intent and reviewability.random and thinking that makes it secure.Evidence: summary.json, summary.txt.
One Linux box (/dev/urandom backed). Not a NIST CSPRNG audit. Free-threaded / alternate RNG builds may differ.
For bulk CSPRNG bytes, secrets.token_bytes ~552.7 MB/s ≈ os.urandom. For non-crypto noise, getrandbits is fine — but never for secrets. Prefer secrets in security paths even when urandom wins a few ops/s at 16 B.
Lab evidence
Lab 1 Oct 2026 IST. Python 3.13.5. 1MiB: secrets 552.7 MB/s ≈ urandom 551.9; getrandbits chunked 48.5. 16B: secrets 1461976/s vs urandom 3322269/s. random NOT for secrets. Differs from lab 41 uuid framing. Affiliates: 0. Evidence: lab-evidence/88-secrets-vs-urandom.
Hands-on itertools.islice vs list slice window lab: real ops/s taking ranges from sequences, measured on Linux localhost in this hands-on lab for SREs.
Lab1 Oct 20264 min read
Hands-on heapq.merge vs sorted(chain) multi-way merge: real records/s on pre-sorted lists, measured on Linux localhost today in this hands-on lab for SREs.
Lab1 Oct 20264 min read
Hands-on mmap MAP_SHARED write+msync vs pwrite region update: real MB/s with durability labels, measured on Linux localhost in this hands-on lab for SREs.
Lab1 Oct 20264 min read