4.6 Benchmarks: measured comparisons
Chapter 4: Reference · Section 6 · Guide map
These measurements were collected on 20 September 2026 using the standalone DLL-loaded benchmark runner. They describe this local build and workload, not the older downloadable release or every possible seed/source combination.
Run the benchmark
Download the Windows x64 benchmark build, extract it, then double-click RandEXomBenchmark.exe beside its included RandEXom.dll. The menu lists available methods, performance/memory measurements, quality diagnostics, smoke checks, settings/injection, and byte export. Settings let you select a DLL, source, seed type, constructor JSON, item count, samples, and report directory. Command-line use remains available:
.\RandEXomBenchmark.exe performance --dll RandEXom.dll --filter RandomLib --samples 25
.\RandEXomBenchmark.exe quality --dll RandEXom.dll --bytes 262144
The Windows x64 executable needs the .NET 8 runtime, not an SDK or target project. It discovers compatible public classes/methods and compiles delegates before timing. See the runner guide in the repository for publishing, injection rules, supported signatures, and export. Use TestOut to watch behavior instead of measuring it.
Measurement setup
- Release DLL built from the current source; fixed performance seed 12345.
- Windows 10.0.19045, x64 process, .NET 8.0.28, 12 logical processors; stopwatch frequency 10,000,000 ticks/second.
- Three separate process runs for each group, each with three warmup batches and 25 measured samples per workload.
- Tables report the median of the three per-run medians, with the smallest/largest per-run median beside it. These ranges are observed variation, not confidence intervals. Allocation columns average the reported managed bytes per call across runs.
- Sources use their default seed algorithms: NetRandom with its internal constant seed, ModuloRandom with XORShift64Seed, SSRNGRandom with default LCG parameters. No custom seed injection was supplied for source comparisons.
- Numeric comparisons use [0, 1,000,000); byte calls fill a reused 1,024-byte buffer. Each measured source sample contains 4,096 calls.
- The desktop was not isolated from other activity. Measurements include delegate/loop overhead and can vary with JIT tiering, GC, and scheduling. Some integer samples are below one millisecond; treat small differences cautiously and inspect
MinSampleMsin the reports.
DLL assembly version: 0.1.5.0. SHA-256:
E45424D9CA2E6D66BAAC82135AF55CE8C62943880E4F77D0C7595A732DC282C6
Random-source comparison
| Source | Operation | Median ns/call | Run-median range (ns) | Calls/second from median | Allocated B/call |
|---|---|---|---|---|---|
| NetRandom | NextInt | 31.54 | 31.10–31.96 | 31702786 | 0.00 |
| NetRandom | NextLong | 75.59 | 74.51–89.16 | 13229974 | 32.00 |
| NetRandom | NextBytes (1,024 bytes) | 4472.58 | 3768.29–4643.51 | 223584 | 0.00 |
| ModuloRandom | NextInt | 483.81 | 393.46–596.00 | 2066912 | 168.00 |
| ModuloRandom | NextLong | 428.56 | 389.72–719.14 | 2333371 | 168.00 |
| ModuloRandom | NextBytes (1,024 bytes) | 4048.68 | 3997.90–4612.35 | 246994 | 0.00 |
| SSRNGRandom | NextInt | 379.69 | 313.62–546.17 | 2633745 | 288.61 |
| SSRNGRandom | NextLong | 395.61 | 366.09–403.37 | 2527771 | 288.61 |
| SSRNGRandom | NextBytes (1,024 bytes) | 336893.07 | 330420.12–340202.69 | 2968 | 294925.12 |
NetRandom had the lowest bounded-int and bounded-long median times in this configuration. ModuloRandom and NetRandom byte-fill timings overlap across runs. SSRNGRandom byte filling incurred substantially more managed allocation here. These results compare complete public APIs, including their range conversion and seed progression, rather than raw generator transitions. To convert byte-fill calls/second to bytes/second, multiply by 1,024.
Item-picker comparison
All rows use 256 distinct integer items, ModuloRandom with its default seed generator, and no initial shuffle. The tree has two branches and three steps (eight endpoints, initially 32 items each). Each sample starts with a fresh full pool outside the timed region and measures exactly 256 pulls; removal samples include endpoint locking as the pool drains. Construction and refill costs are excluded from this table.
| Picker | Behavior | Median ns/pull | Run-median range (ns) | Allocated B/pull |
|---|---|---|---|---|
GachaR<int> |
Remove | 504.30 | 485.16–650.39 | 168.31 |
GachaRBatched<int> |
Remove | 7047.66 | 6807.42–9217.19 | 1252.41 |
DistributedTreeR<int> |
Keep | 1694.92 | 1669.53–1724.61 | 704.00 |
DistributedTreeR<int> |
Remove and lock | 1785.94 | 1736.33–2630.86 | 699.59 |
GachaR was fastest for this small, distinct-item removal workload. This does not establish the best choice for large repeated weights: GachaRBatched stores counts, while the tree deliberately chooses available branches before items. The keep row permits repeats and is not a without-replacement alternative. Equal branch choices also mean the tree is not a uniformly shuffled bag after endpoints start emptying.
Randomness diagnostics
Each source was checked with 262,144 bytes and seeds 1, 12345, and 987654321. All nine source/seed cases reproduced their byte sequence and had zero range-check failures. The table shows seed 12345; all seeds, bit-balance/runs statistics, pair-frequency statistics, and integer-distribution statistics are in the downloadable report.
| Source | Byte chi-square | Entropy (bits/byte) | Lag-1 correlation | Int chi-square |
|---|---|---|---|---|
| ModuloRandom | 324.88 | 7.999108 | -0.003292 | 300.24 |
| NetRandom | 274.95 | 7.999242 | 0.002673 | 214.81 |
| SSRNGRandom | 265.68 | 7.999269 | -0.000144 | 252.99 |
For 256 uniform buckets, the chi-square statistic is expected around 255, not zero. Entropy approaching 8 and low lag-1 correlation are descriptive diagnostics, not proof of independence or a quality certification. No p-values or overall pass/fail quality scores are claimed. Constrained helpers and item-removal sequences are not evaluated under the same independent-uniform assumptions.
Download the measurements
The measurements are unchanged from the runner output; only absolute local DLL paths in JSON were normalized to repository-relative paths. Each JSON includes the DLL hash and environment. These archived results remain a dated snapshot when the code changes.
| Group | Run 1 | Run 2 | Run 3 |
|---|---|---|---|
| sources | JSON · CSV | JSON · CSV | JSON · CSV |
| items | JSON · CSV | JSON · CSV | JSON · CSV |
Quality: JSON · CSV. Item constructor settings: items.json.
Reproduce the comparison
Use the same DLL hash for an exact build comparison, or record the new hash when benchmarking another build. Put the runner, DLL, and downloaded items.json in one folder. Run the source and item commands three times into distinct output folders, then run quality once:
foreach ($run in 1..3) {
.\RandEXomBenchmark.exe performance --dll RandEXom.dll --filter RandomLib --samples 25 --output "results/sources-$run"
.\RandEXomBenchmark.exe performance --dll RandEXom.dll --filter Framework.Item --random ModuloRandom --items 256 --config items.json --samples 25 --output "results/items-$run"
}
.\RandEXomBenchmark.exe quality --dll RandEXom.dll --bytes 262144 --output results/quality
← Previous: 4.5 TestOut · Guide map · End of guide