Repository navigation
flashnext cuda: the grouped draft head's lanes carry their page table - #2244
Conversation
|
Thank you, @Arminova. That's a clear find, and the receipt makes it easy to follow. We've read it against
On #1444: we'd like this to land on its own now, so please keep it open. #1444 can take the line when it next rebases, since its We have no asks. Our own GB10 run is queued: |
|
/atc exchange a20c200f48b230cc3da200edc11af84bc0df985b0fd7e237a54888b60273be02 |
|
/atc exchange a515bb94031949baebac3b378caa71cc2938c0e9458d991e98a251419b139adf |
|
@Arminova, our GB10 run is in, and it confirms the fix. Setup:
Paging on (
The sweep (paging on;
Paging off: both builds give the same replies at the same speed in every cell. No server log shows a window graph falling back to eager. Since this ran, |
On TF_FLASHNEXT_KV_PAGES, a shared round with two or more drafting streams ran the MTP head over one grouped pass whose lanes had no page table, so the head wrote and read its rows as if its cache were contiguous from the first page: into other streams' pages. With three equal 16K prompts at once, one reply of three differed from its solo run. The lone head's pass already carries it. The grouped lane is built in headLane now, and a host test checks that it carries its draft's page table, length and rows (without `.pg` it fails: expected 28672, found 0).
dc96e98 to
02e10a4
Compare
|
Thank you @Arminova. 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. |
|
Thanks @Arminova! This is in TensorFold 1.2.4, out now: https://github.com/ashhart/TensorFold/releases/tag/v1.2.4 |
What this changes
With
TF_FLASHNEXT_KV_PAGESset, a shared round where two or more streams draft runs the MTP head over one groupedpass (
headGroupincuda_mtp_ops.zig). Its lanes were built without the draft's page table, so every paged lane'shead read and wrote its cache rows contiguously from the pool's first page: the same addresses for each of them, in
pages other streams own. A lone head's pass already carries
.pg; the grouped lane now does too. One commit, onefile, no kernel changes. Without paging
d.pgis the contiguous default, so default settings keepmain's bits.On a GB10 with paging and three streams (receipt below),
main's concurrent replies differed from their runs alonein 1 of 18 (FP4) and 5 of 15 (MLX) rows a server, and this change decodes 2-24 % faster there; one stream and the
default settings are level.
How it came about: the grouped head pass came in
5e77d736c, then Flash Next's KV pages in5af5af721, which set.pgon the lone head's pass; the grouped lane kept its form from before paging.main002c9e03e; merges clean with today'smain9200a3064(10 commits, none in Flash Next).atc overlap --json: not run (ATC is not installed here). The same test by hand,git merge-treeof this commitagainst each of the 12 open pull requests whose file lists touch
cuda_mtp_ops.zig: clean with EXL3 Flash Next: expert-parallel TP=2 (same tokens as TP=1) and MTP drafts (2/8) #1442, EXL3 Flash Next: deep prefill (3/8) #1443, flashnext cuda: prompt cache marks inside a chunk are kept on the way instead of cutting the pass #1866and flashnext cuda: beside another open stream a greedy chain stops at 0.6 per draft; wide rounds draft it 6 deep #2160; one new conflict,
cuda_mtp_ops.zigon this line, with EXL3 Flash Next: KV pages from the core pool (4/8) #1444 and the ones stacked on it (EXL3 Flash Next: CUDA graphs and the device planner (7/8) #1445, EXL3 Flash Next: draft lane guard for offloaded experts (8/8) #1446,EXL3 Flash Next: host-resident K/V on the tier (6/8) #1522); Flash Next TP=2: exact output-column splits, 21% faster plain decode #1784, Flash Next shared down: remove the scatter with direct slot output #1792, Flash Next BF16 prompts: larger CUDA tiles with identical output bytes #1846 and Flash Next BF16 prompts: reduce shared staging with K32 loads #1848 already conflict with
main, and this commit adds no file to theirs.Draft.headSeqthat carries.pg, so itfixes the same line inside its larger change. This PR is the fix alone for
mainnow; whichever lands second takesthe other's line. @ashhart, if you'd rather carry this line in a fix-up of your own or let EXL3 Flash Next: KV pages from the core pool (4/8) #1444 bring it, that's
fine with me, and I'll close this one.
Checks on this head
dc96e9869.zig build test-cuda-host311 / 311 steps pass. The new test, "a grouped head's lane carries its draft's pagetable" (
cuda_mtp_ops.zig, run bytest-cuda-host), fails with the lane built as onmain:expected 28672, found 0(1 failed of 4,874; no other failure), and passes with this change.zig build test -j10: 537 / 537 steps, 0 failed (2,774 tests in the steps that rebuilt, 37 skipped).zig fmt --check zig build.zig: clean.Receipt
Environment: GB10, driver 580.173.02, nvcc 13.3.73,
nvidia/Qwen3.8-Flash-Next-FP4-Localatb9ce18a6andVontra/Qwen3.8-Flash-Next-MLX-4bit-MTPatdadefa80, n-gram table warmed before each start, a fresh server for eachrun, order base-new-new-base. Base
main002c9e03e; this changedc96e9869.Paging on (
TF_FLASHNEXT_KV_PAGES=256,--parallel 3), three equal prompts at once (tools/bench_cache.py --streams 3, 256 tokens; FP4 at--context 262144, each cell twice a server; MLX at--context 65536, 2K threetimes, 8K and 16K once). Concurrent replies that differ from the same prompt's reply alone, and decode a stream in
tok/s; two numbers = the two runs of each build:
mainmainA first MLX pair run once each, not alternated, had
main's 2K cell at 33.7 against 30.3; the alternated rerunabove replaces it (same build, same cells).
Paging on, different prompts (
tools/bench_concurrent.py --alone --serial --levels 1,3 --temperatures 0,1, 256tokens): on both builds every concurrent reply equals its run alone and in series, and the reply digest is the same
in every run. tok/s:
mainmainWith this change, FP4's paged 3-stream rows are back at its unpaged speed (code / chat greedy 174.8 / 174.6 and
113.7 / 114.1 below). One stream never runs the grouped pass, so it is level.
Default settings (FP4, no paging, 262144 x 3): replies identical on all four servers (
bench_cache2K / 8K / 16Kalone, first and resumed; drafted replies of four texts of 283-17,182 tokens, greedy and sampled;
bench_concurrent --alone --serial1 and 3 streams). Speed level: 3 streams code / chat greedymain174.8 / 174.6 and 113.7 /114.1, this change 173.2 / 174.2 and 113.5 / 113.9; one stream 72.0 / 72.3 and 71.7 / 72.6. Code greedy at 3
streams is the one cell with both new runs below both base runs (-0.6 %); without paging the lane is byte for
byte the one
mainbuilds (.pgat its default), so the GPU work is the same.Drafted replies equal
"draft": false: the four texts' greedy hashes equal those of a"draft": falserun of thesame texts in an earlier receipt. A resumed prompt equals a fresh one: every
bench_cacherow resumed from thecache (2K / 8K / 16K, both builds, paging on and off) equals its first run.
Prompt speed: n/a, the change is in the draft head's decode lanes. First tokens in the cells above are level (2K
from an empty cache, no paging, two requests a server:
main1,265 · 1,129 / 1,136 · 1,137 ms, this change1,127 · 1,136 / 1,130 · 1,139 ms).
Licence of new weights: n/a.
Checklist
"draft": false, a resumed prompt equals a fresh one, and concurrent equals solo.the one -0.6 % cell above.