Repository navigation
nvfp4 experts waves: the contract's two-wave shape grows past the loaded entry's resident warps - #1832
Merged
Conversation
…ded entry's resident warps The waves kinds' largest shape was a fixed 1,024 rows: 128 or more items of 16 pairs, 2,560 or more gate-up units. An RTX 5090 loads nvfp4_expert_waves<2, 2, 4> at 4 CTAs an SM, 2,720 resident warps, so the whole gate-up plan ran in one wave and the cut_waves control, with nothing past the first wave to drop, stayed green. Each waves kind now serves 8 x (resident warps / column blocks + 1) rows, at least 1,024, read from the entry it launches as loaded (its 4- or 1-warp CTAs): one item more than a full wave holds. Each control keeps its twin's shapes. The half entry is unchanged; it serves only shapes that fit one wave. A host test runs Experts.kernel() on synthetic residencies: a GB10-like one keeps 1,024 rows, the 5090's as loaded, 8,160 warps, and warps that 1,024 rows fill exactly (1,032 rows). It fails on the fixed shape.
ashhart
force-pushed
the
fix-1778-waves-witness
branch
from
November 2, 2026 05:31
3317446 to
007f8c6
Compare
Owner
|
Thank you @rootsbymenda. This is merged into main with your commits under your own name, so you're listed as a TensorFold contributor. It ships in the next release, credited to you in the changelog and the release notes. |
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.
What this changes
On an RTX 5090 the kernel contract's
nvfp4_expert_waves<2, 2, 4>cutoff control stays green (#1778). The waves kinds' two-wave shape was a fixed 1,024 rows, and the loaded gate-up waves entry holds all of that shape's units in one wave, so the control drops nothing past the first wave. Each waves kind's largest served shape is now sized from the residency the loader measured for the entry it launches, at its 4- or 1-warp CTAs:8 x (resident warps / column blocks + 1)rows, never fewer than 1,024. That is one item more than a full wave holds.What the loader measures here, with nvcc 13.0 (the issue's run used 13.3):
cut_wavesrunsup_waves's, andcut_waves_onerunsdown_waves_one's. The half entry keeps its shapes, because it serves only shapes that fit one wave.Experts.kernel()now returns an error union andGroup.createpasses the error on. The sized shapes live in the fixture (Experts.served), which the group owns for the whole run.Fixes #1778
Stacked on: nothing (based on main
a8c02eef2; merges cleanly with main1dfc78c55).Receipt
Environment, with the TensorFold commit, MLX or PyTorch version, machine and GPU, checkpoint and revision: head
331744663on maina8c02eef2; Zig 0.17.0; kernels built with nvcc 13.0.88, the release compiler. x86_64: Ubuntu 24.04.5, RTX 5090 (sm_120, 170 SMs), driver 595.99.02. aarch64: DGX Spark, GB10 (sm_121, 48 SMs), driver 580.178.04. No checkpoint is needed, because only the contract fixture changes.Exactness, from
tools/bench_concurrent.py --alone --serialortoken_shaagainst"draft": false: n/a. No kernel, sum or token path changes.Decode speed before and after, from
tools/bench_openai.py: n/a, for the same reason.Prompt speed before and after: n/a, because the change can't reach prompt processing.
Host test:
contract_nvfp4_waves_test.zig, imported fromcontract_arch_test.zig, sotest-cuda-hostruns it. It builds the real fixture'sExperts.kernel()over loaded kernels with synthetic residency. For every waves kind at its 4- or 1-warp CTAs, the largest served shape must route more units than the entry's resident warps hold, counting at least rows / 8 items, however the router spreads them. Each control's shapes must also equal its twin's. It runs four residencies:On main, the same file fails three of them:
The controls on the RTX 5090.
kernel-contracthas no per-set filter, and on the 5090 it's CPU-bound here (float64 references), so a full run didn't fit. Instead, a local command that isn't part of this PR runscontract_nvfp4_tile.Group's 18 registrations alone throughgates.runAll, with the contract's seed. It also prints the loader's residency and each waves kind's real item count. Results:On the GB10 the same command at head gives 14 of 14 kernels passing and 4 of 4 controls red, at 1,024 rows, the shapes main runs there.
Mutation: this change with the sizing returning the old 1,024 rows. The host test fails the same three cases, and on the 5090
CONTROL MISSEDcomes back fornvfp4_expert_waves<2, 2, 4>(130 of 130 items in the first wave).The full contract on the GB10 (
tf-cuda-test kernel-contract, built with-Dnvcc=... -Dsm=121):The run exits 1 on the limit problems alone. Every gate FAIL line pairs with its CONTROL line (344) or KNOWN line (6). The 6 known findings are the ones registered on main.
The 181 limit problems are main's. On this GB10, main's run and head's give the same verdict lines, line for line, timings aside: every PASS, FAIL, CONTROL, KNOWN and limits line matches. The waves shapes on this device are unchanged, so that's expected. This change touches no kernel, record or build file. They are:
qwen4_hc48,qwen4_ple40,qwen4_gdn20,qwen4_projection20,qwen3515,qwen35_kv815,nvfp4_experts_tile10;qwen35_kv86,qwen354,nvfp4_experts_tile2,qwen35_keep1.Tests run, with pass, fail and skip counts (x86_64 Linux, each step from an empty cache):
Main's one failure is
exl3_load.test.a scan that fails an allocation frees every group prefix it held, which returnserror.NondeterministicMemoryUsagefromcheckAllAllocationFailures. It's intermittent. It failed in both of main's runs above and in both runs of this change with the sizing mutated back. It passed in two more runs of main that had only the new test added, and in head's runs. This change doesn't touchexl3_load.zig.atc overlap:contract_nvfp4_fact.zigis shared with Flash Next shared down: remove the scatter with direct slot output #1792, andcontract_arch_test.zigwith cuda: tune dense 27B MLP tiles on RTX PRO 4500 #1730 and exl3 cuda: the 27B's EXL3 speed series on the native Dense module (#260), stacked on #1465 #1467. Checked withgit merge-tree: our files merge cleanly with Flash Next shared down: remove the scatter with direct slot output #1792 and cuda: tune dense 27B MLP tiles on RTX PRO 4500 #1730. exl3 cuda: the 27B's EXL3 speed series on the native Dense module (#260), stacked on #1465 #1467 conflicts with main incontract_arch_test.zigand three other files, and the conflict list is the same with or without this branch.Not run, and why:
kernel-contract: it's CPU-bound here, so the full receipt is the GB10's, and the 5090 receipt is the group run above;Checklist
"draft": false, a resumed prompt equals a fresh one, and concurrent equals solo. (Not measured; no token path changes.)SUMMARYline is in the receipt. (No kernel changes; the GB10SUMMARYline is above.)