Each row is one run of the fixed standard. Reproduce with:
python speedrun.py --config configs/standard_8xh100_qwen72b.yaml
See docs/SPEEDRUN.md for the standard, the metric
(held-out FineWeb bits-per-byte), and the methodology. Rows are appended
automatically by speedrun.py; seeds/s is the optimization throughput.
Validated on NVIDIA L4 (torch 2.12+cu130, triton 3.7) via Modal — full record
in speedrun-runs/gpu_kernel_validation/record.json:
-
tests/test_kernel_gpu.py: 7 passed — Triton kernel == pure-torch reference bit-for-bit (fp32/bf16/fp16), GPU signs == CPU signs, switch≡reconstruct, drift-free reconstruction. -
Weight-switch throughput (fused reconstruct vs. upstream perturb+restore), measured bf16 on the L4 (
modal run scripts/modal_smoke.py --tier throughput):elements old perturb+restore fused reconstruct speedup 50M 5.17 ms 0.91 ms 5.7× 200M 20.66 ms 3.46 ms 6.0× Conservative: "old" is just upstream's 2×-
randnperturb+restore; the real runtime additionally drops the per-callempty_cache/synchronizeand the entire restore pass. Larger GPUs (H100) widen the gap (memory-bandwidth bound).
Full pipeline on NVIDIA L4 via Modal (modal run scripts/modal_smoke.py --tier 2), config configs/smoke_1gpu_small.yaml, Qwen2.5-1.5B-Instruct,
population 16, GSM8K (32 train / 32 test) — record in
speedrun-runs/modal_smoke/record.json:
| metric | base | ensemble (K=4) |
|---|---|---|
| GSM8K test accuracy | 28.1% (9/32) | 40.6% (13/32) (+12.5 pp) |
| held-out FineWeb bpb (lower=better) | 0.2665 | 0.2678 |
- Ensemble lifts the task metric by +12.5 pp — the majority vote over fused-Rademacher-perturbed models works end-to-end through the fast runtime.
- FineWeb bpb is essentially flat (ensemble marginally worse). Expected: at this tiny scale (1.5B, only 16 seeds, σ∈{1e-3,2e-3}) selection optimizes GSM8K reward, not language modeling, and held-out web text is far from the GSM8K distribution — there is no reason a 16-seed task-selected ensemble should improve generic LM bpb. The metric is wired through correctly; it simply doesn't move at smoke scale. Whether the 72B standard moves bpb is the open empirical question this harness exists to answer — not something to assume.
- Throughput 0.19 seeds/s (16 seeds in 83 s) on a single L4,
enforce_eager, greedy. This is a tiny smoke, not the 8×H100 standard.
seeds/sec is the optimization rate; gen-tok/sec and prompts/sec are the
inference rates the harness now logs by default (amortized over the
base+sampling+ensemble generation phases). From the --probe_top 3 L4 run:
- 0.20 seeds/sec, ~1.3–1.4k generated-tokens/sec, ~5.9 prompts/sec (≈2.0k total tokens/sec incl. prefill) on a single L4, eager, greedy.
- Per-seed
--probe_top 3(each top seed evaluated standalone, not voted): the best seed alone reached 37.5% test acc (vs 28.1% base), but the #3 seed dropped to 18.8% despite equal train reward — i.e. high train reward doesn't guarantee held-out gains; the K=4 ensemble (40.6%) beat every individual seed. (bpb per seed ≈ 0.270, flat vs base 0.2665, as expected at this scale.)
Qwen2.5-7B-Instruct on a single H100-80GB, enforce_eager=false (configs/ tuned_1xh100.yaml, 256 seeds), record speedrun-runs/modal_7b/record.json:
- Graphs captured fine: "Capturing CUDA graphs (decode, FULL) 51/51 … finished in 4 s, took 0.59 GiB" — one-time, amortized over the population.
- Correctness under graphs: sampling rewards vary across seeds (0.94/0.95/ 0.97/…), so replayed graphs read the freshly-perturbed weights — in-place reconstruction is compatible with captured graphs at 7B. ✓ (the key claim.)
- Throughput: 0.33 seeds/s, ~5.3k gen-tok/s, ~21 prompts/s (≈7.8k
total-tok/s incl. prefill) — 4× the gen-tok/s of the eager 1.5B run despite
a 4.7× larger model (graphs + H100 +
max_num_seqs=512). - Quality caveat: base is already 90.6% on this 64-sample GSM8K slice and
the K=25 ensemble is also 90.6% (+0.0) — saturated at n=64, not a method
result. A probe seed hit 93.8% individually; the ensemble can't move a
near-ceiling base on so few samples. FineWeb skipped (config
build_if_missing: false, no slice prebuilt). Use a larger test slice to get a real ensemble Δ.
Qwen2.5-7B on 1×H100, graphs ON, 512 seeds, GSM8K 128 train / 256 test,
held-out FineWeb = real 256-doc slice (sha 4682737b…), configs/run_512_7b_h100.yaml,
record speedrun-runs/modal_run512/record.json:
- Ensemble gain (both metrics improve):
- GSM8K: base 89.06% (228/256) → K=25 91.80% (235/256, +2.73 pp), K=10 91.41% (+2.34 pp).
- Held-out FineWeb bpb: base 0.5932 → ensemble 0.5918 (lower=better, over 205k scored tokens / 1.02 MB). Small but the right direction — the task-selected ensemble nudged generic LM held-out loss down, not up.
- Throughput (512 seeds): 0.28 seeds/s, 9,045 gen-tok/s, 36.1 prompts/s (18.1M gen-tok; ~13.3k total-tok/s incl. prefill). Higher gen-tok/s than the 256-seed run — wider batch over 128 train prompts saturates the H100 better. Sampling rewards varied 0.92–0.98 across all 512 seeds → graph+perturbation compatibility holds at full scale. ✓
- Per-seed probes (top 5, evaluated standalone): individual test acc ranges 87.9–91.0% (vs 89.06% base) — i.e. some single seeds beat base, some don't, and none reliably beats the 91.8% ensemble; per-seed bpb 0.596–0.649, all worse than the ensemble's 0.592. Voting helps on both metrics. Best σ=0.0005.
- Wall-clock 2,915 s total (sampling 1,861 s · ensemble 134 s · probes 375 s · FineWeb 222 s · launch 306 s).
The earlier
77e1977attempt produced the accuracy/throughput above but crashed in FineWeb (a held-out chunk tokenized to exactlymax_model_len, and the bpb pass requests 1 output token →prompt_len+1 > max_model_len). Fixed ine724a9a(speedrun._fineweb_max_lenclamps chunk len tomax_model_len−2, + regression test); this re-run completed the bpb.
| date | commit | config | model | hardware | pop | seeds/s | gen-tok/s | prompts/s | base acc | ens acc | base bpb | ens bpb | total |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2026-05-30 | bf6e80b | smoke_1gpu_small | Qwen2.5-1.5B-Instruct | 1×NVIDIA L4 | 16 | 0.19 | — | — | 28.1% | 40.6% | 0.267 | 0.268 | 472s |
| 2026-05-30 | 36abea5 | smoke_1gpu_small (+probe) | Qwen2.5-1.5B-Instruct | 1×NVIDIA L4 | 16 | 0.20 | 1316 | 5.88 | 28.1% | 40.6% | 0.267 | 0.268 | — |
| 2026-05-30 | 27c5438 | tuned_1xh100 (graphs on) | Qwen2.5-7B-Instruct | 1×H100-80GB | 256 | 0.33 | 5296 | 20.9 | 90.6% | 90.6% | — | — | 1021s |
| 2026-05-30 | e724a9a | run_512_7b_h100 (GSM8K) | Qwen2.5-7B-Instruct | 1×H100-80GB | 512 | 0.28 | 9045 | 36.1 | 89.1% | 91.8% | 0.593 | 0.592 | 2915s |
| 2026-05-30 | c76e5f5 | run_512_7b_math500hard | Qwen2.5-7B-Instruct | 1×H100-80GB | 512 | 0.08 | 3706 | 5.39 | 60.4% | 68.2% | 0.593 | 0.592 | 7653s |
| 2026-05-30 | 7a9418f | run_512_7b_math500hard_lowsigma | Qwen2.5-7B-Instruct | 1×H100-80GB | 512 | 0.08 | 3397 | 4.90 | 62.0% | 69.3% | 0.593 | 0.593 | 6693s+ |
Same 7B / H100 / graphs-on / 512-seed setup, only the task changed: MATH-500
levels 4-5 (the hardest 262 problems, 64 train / 192 test disjoint) instead of
GSM8K. configs/run_512_7b_math500hard.yaml, record speedrun-runs/modal_math512/.
-
The ensemble lift nearly tripled vs GSM8K — because the base has real headroom (60%) instead of GSM8K's 89% ceiling:
task base acc ensemble K=25 Δ GSM8K (saturated) 89.06% 91.80% +2.73 pp MATH-500 lvl4-5 60.42% 68.23% +7.81 pp K=10 also +6.77 pp (67.19%). This is the payoff of picking a hard-but-not-floored task (not AIME, which would be ~0-7% and noise-dominated).
-
FineWeb bpb still improves: base 0.5931 → ensemble 0.5920 (held-out, 205k tokens). Both metrics move the right way, as on GSM8K.
-
σ sweep (per-σ mean train reward, ~170 seeds each): 0.0005→0.6192, 0.001→0.6157, 0.002→0.5844; best σ=0.0005 again. Note: on this harder task even the best σ's mean sits just below base train (0.625) — the average perturbation is mildly harmful at every σ; the win comes from the right tail + voting. (This motivated a parallel low-σ {0.0005, 0.0001} run to probe below 0.0005.)
-
Probes: top individual seeds 55.7-62.5% test (vs 60.4% base) — some beat base, some don't; none reach the 68.2% ensemble. Voting is doing the work.
-
Throughput much lower than GSM8K (0.08 vs 0.28 seeds/s, 3.7k vs 9k gen-tok/s) because MATH generates full 2048-token solutions — that slowness is real compute, not a stall. Total 7,653 s (sampling 6,586 s is the bulk).
Identical to the run above EXCEPT the sigma grid = {0.0005, 0.0001} (~259/253 split),
configs/run_512_7b_math500hard_lowsigma.yaml, record speedrun-runs/modal_mathlow512/.
- Going below 0.0005 does NOT help — it mildly hurts. Per-sigma mean train reward: 0.0005 -> 0.6160 (n=259, sd 0.033, 27% of seeds above base), 0.0001 -> 0.6115 (n=253, sd 0.028, 22% above base). 0.0005 edges 0.0001 by 0.0045 = ~1.7 SE of the difference: suggestive, not decisive, but it leans the wrong way for going smaller. 0.0001 also has fewer above-base seeds and a tighter spread — the "too gentle" signature (rounds away on larger weights; ~25x weaker KL). So the smaller-is-better trend from {5e-4,1e-3,2e-3} bottoms out around 0.0005 rather than continuing.
- Ensemble still wins big: base 61.98% -> K=25/K=10 69.27% (+7.29 pp), matching the main sweep's +7.81pp (both ~3x GSM8K). FineWeb bpb 0.5931 -> 0.5926.
- Caveat (run-to-run nondeterminism): base acc here is 61.98% vs 60.42% in the main run — same model, same disjoint slice, same greedy decode. vLLM is not bit-reproducible under CUDA graphs + continuous batching, so base eval itself wobbles a few problems. The +7.3/+7.8pp ensemble gains are large vs this noise, but absolute numbers carry ~+/-1.5pp run-to-run uncertainty.