linalg/qr: regenerate timed inputs per repeat and mirror the stream rule - #170
Open
SamMausberg wants to merge 1 commit into
Open
linalg/qr: regenerate timed inputs per repeat and mirror the stream rule#170SamMausberg wants to merge 1 commit into
SamMausberg wants to merge 1 commit into
Conversation
Close the remaining gaps from gpu-mode#148 in qr_py and qr_v2 (gap 1 was fixed in gpu-mode#150). The two eval.py files stay byte-identical. Gaps 2+3, timed-region output replay: the timed loop reused the same input objects on every repeat, and the geomean-dominant shapes have a data_list of length 1. A kernel could compute outputs during untimed calls (the warmup invocation, the pre-timing pass, earlier repeats), cache them by id() or content, and replay them inside the timed window. recheck=True does not catch this case, because a replayed output is still correct for that same input. Each timed repeat now regenerates fresh input content from a seed shift that is unique per (invocation, repeat, item), and warmup and timed invocations draw from disjoint seed_salt ranges. Untimed content therefore never reappears in a timed window, including for a cache persisted to disk across the test / benchmark / leaderboard runs of one submission. Generation and cloning happen outside the timed CUDA-event window, before clear_l2_cache(), and the warmup / pre-timing batch is bit-for-bit unchanged. The stability break (err/mean < 0.001) now requires a minimum of 10 timed samples. The regeneration and recheck work between repeats pushes wall time past the 0.1s arming threshold almost immediately on large shapes, and without a floor the break can fire on a 3-sample error estimate. The mean*runs and 120s budget caps are unchanged. An element-wise cross-check against torch.geqrf was considered and rejected: (H, tau) is not unique (sign conventions, blocking, inner precision), so exact-match checking would reject valid implementations that the tolerances are meant to admit. Behavior changes worth knowing about: a kernel that clobbers its input buffer in-place now passes benchmark/leaderboard (each repeat gets a fresh buffer; test mode already handed kernels a clone), and "mixed" benchmark lines draw a fresh profile assignment per repeat, so their mean ranks a distribution of instances rather than one fixed instance. Gap 4, the stream rule: KernelBot rejects any submission containing the substring "stream" (case-insensitive, comments included) at intake, so a stream-using kernel passes every local mode and only fails on its first remote submission. eval.py now applies the same substring test to the submission source before dispatching any mode and fails with a report entry plus the reason on stderr. Only the submission file is scanned, matching the remote rule. Validated on an RTX 5070 Ti (torch 2.8.0+cu128); this checks harness semantics, not B200 performance. old = main, new = this change, on a count=1 shape (batch 256, n 512), benchmark mode: torch.geqrf reference pass, 448.0 ms/input pass, 447.0 ms/input shape-keyed output cache pass, 5.3 us/input fail, first timed recheck content-keyed output cache pass, 203 us/input pass, 448.8 ms/input id()-keyed output cache pass, 3.0 ms/input fail, stale replay caught in-place input clobbering fail at repeat 2 pass side-stream kernel pass locally fail in every local mode Reference means agree to 0.2% between harnesses on that shape. On a large mixed shape the timed sample count went 44 (main) -> 3 (without the stability-break floor) -> 37 (with it). Reference and legitimate-optimization kernels (fp64 inner math whose factors differ element-wise from fp32 geqrf; in-place input clobbering) pass test and benchmark modes on the new harness, and the reference also passes leaderboard mode. Seed-shift uniqueness was checked exhaustively for all benchmark-line base seeds (1.25M derived seeds per line, distinct and disjoint from the warmup sequence); with a server seed the shifted values wrap mod 2^63 and disjointness is negligible-probability-of-collision rather than exact. Developed and validated in combination with Claude Fable 5. Closes gpu-mode#148.
SamMausberg
force-pushed
the
qr-validation-gaps-148
branch
from
August 16, 2026 17:56
5dbd0b4 to
f715eec
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #148 (gaps 2, 3, and 4; gap 1 was fixed by #150).
qr_py/eval.pyandqr_v2/eval.pyget the same change and stay byte-identical, as onmain.Gaps 2+3: output replay in the timed region
The timed loop reused the same input objects on every repeat, and the shapes that dominate the leaderboard geomean have a
data_listof length 1. A kernel could compute outputs during untimed calls (warmup, the pre-timing validation pass, earlier repeats), cache them byid()or content, and replay them inside the timed window at near-zero cost. The recheck from #150 cannot catch this, because a replayed output is still correct for that same input. This is thecapped_sleepy_cachemechanism named in the issue.Every timed repeat now gets fresh input content:
_timed_repeat_batch()derives per-item seeds through the existing_combine, from a shift unique per (invocation, repeat, item).% 2**63keeps the nested result inside whatmanual_seedaccepts. Disjointness is exact for local task seeds (verified exhaustively); with a server-combined base seed the mod can wrap, making collisions negligible-probability rather than impossible._run_single_benchmarkinvocation gets its ownseed_salt(benchmark: warmup 0, timed1+idx; leaderboard: warmups1..N, timedN+1..2N). Untimed content never reappears in a timed window, even for a cache persisted to disk across the test, benchmark, and leaderboard runs of one submission.clear_l2_cache(), outside the timed CUDA-event window, so per-repeat timing is unchanged. The warmup / pre-timing batch is bit-for-bit identical to today.An id-keyed cache never hits (fresh objects), a content-keyed cache never hits (fresh content), and a stale replay fails the existing recheck.
Not added on purpose: an element-wise cross-check against
torch.geqrf.(H, tau)is not unique (signs, blocking, inner precision), so exact matching would reject valid implementations the tolerances are meant to admit.Stability-break floor
Found in A/B runs: the untimed per-repeat work arms the early-stop rule (
err/mean < 0.001once wall time passes 0.1s) almost immediately, and on a large shape it fired on a 3-sample estimate (runs: 44 -> 3). The break now needs at least 10 timed samples; the same shape then takes 37. Themean*runsand 120s budget caps are untouched, so worst-case wall time is bounded as before.Behavior changes
mainthey failed at repeat 2. Test mode always handed kernels a clone, so this aligns the modes.mixedbenchmark lines draw a fresh profile assignment each repeat, so their mean ranks a distribution of instances instead of one fixed instance.Gap 4: the stream rule, mirrored locally
KernelBot rejects any submission containing the substring
stream(case-insensitive, comments included) at intake (api_utils.pyandleaderboard_cog.pyin discord-cluster-manager). Nothing checked this locally, so stream-using kernels passed every mode and only failed on their first remote submission.eval.pynow runs the same substring test before dispatching any mode, emits a report entry plus the reason on stderr, and exits 112. Only the submission file is scanned, matching the remote rule.Validation
RTX 5070 Ti, torch 2.8.0+cu128; this checks harness semantics, not B200 performance. Runs staged the real problem files exactly as KernelBot lays them out, on a true
count=1shape (batch 256, n 512), benchmark mode:torch.geqrfreferenceid()-keyed output cacheReference means agree to 0.2% between harnesses; leaderboard mode passes; test mode passes on the real
task.ymlshapes includingmixedand the stress cases. Seed uniqueness was verified exhaustively, and timed batches are deterministic per (salt, repeat) with the warmup batch unchanged.Not measured: B200 wall clock against the task timeouts. Here the three-shape reference benchmark was within 5% of
main(237.6s vs 228.2s), but a B200 number would be worth taking before merge.Follow-ups, out of scope:
eigh_pyandcholesky_pyshare the same timed loop and lack the stream gate, and per Bryce's comment on the issue the gate is worth propagating to the other problems.Made in combination with Claude Fable 5.