Repository navigation
hip flashnext: the MTP head absorbs a prompt chunk on the prompt tiles - #1878
Conversation
|
Thanks @gilby. Routing the MTP head's chunk fill through the prompt path instead of a whole chunk of decode windows is a sensible fix for that 0.54 s, and the kernel trace makes the cost clear. What we need before it lands, since we have no AMD GPU to check it on:
Then take it out of draft and we'll review it for landing. |
7c500d9 to
7169ea5
Compare
|
Thanks. I ran the three checks on the rebased head, and the first one does not hold, so I'm leaving this in draft until you decide.
So the change buys about 3% on cold prompts, and it changes the head's cache bytes without changing a served token. If you want the head's cache to stay bit-equal to decode's, I'll drop this PR. If a draft-only difference is acceptable, I'll take it out of draft with the body updated to say so. Which do you prefer? I also rebased both PRs onto current main: this one is now |
|
Follow-up on the head's cache bytes, with the checks I could add. Head
So the change is about 3% faster on cold prompts, gives the same tokens and about the same acceptance, and does change the head's cache bytes from decode's. If bytes equal to decode's are the bar for this PR, I will close it; if the target's verification is enough, it is ready to review. Which do you prefer? |
|
Follow-up on the resumed-prompt check, which I said I could not run: my earlier runs sent single-message prompts, and a single message has no chat history, so this family's prompt cache (planned chunk starts, a history mark only) never keeps a state. That was a flaw in my test, not in the build. I reran it as a three-turn chat on a prompt of about 7,000 tokens (temperature 0, thinking off, 48 new tokens at most a turn), on main (this PR's parent) and on this head, each on a fresh server, with
This covers the head cache being saved and restored with the prompt state. It is one prompt, so it is a check, not a sweep. The question in my previous comment stands: if bytes equal to decode's are the bar for this PR, I will close it; if the target's verification is enough, it is ready to review. |
|
Thanks @gilby, and thanks for the careful follow-ups. You found exactly where the bytes part: the old path matched We'll take the draft-only difference. The target verifies every served token, replies equal main's at every length Before it comes out of draft:
One heads-up: #2696 also changes |
|
Thanks @ashhart. Both points are done, on head
On the heads-up: #2670 is in main, and its dry run now passes Checks on Environment: Zig 0.17.0, |
|
Thanks @gilby, both are answered. On today's main with #1877's prompt kernel, cold first token is 3.7% to 5.8% shorter from 2K to 59K-token prompts, every reply's
|
|
CI is green on |
After each prompt chunk the draft head runs over the chunk's rows to fill its own caches. It ran as a decode window, so its projections and its MoE took the stream tiles for up to a whole chunk of rows, re-reading each weight once per four rows. A prompt chunk now runs it on the prompt tiles (o.prefill and moe.run's prompt path), as the main layers' prompt rows already do; decode windows keep the window path. The head's prompt-row caches take the prompt tiles' sums, so its drafts can differ in the last bits; replies are verified as before and do not change.
…not from the options
…en gain of its receipt
28ef18b to
8952acd
Compare
|
Thank you @gilby. 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 @gilby! This is in TensorFold 1.2.8, out now: https://github.com/ashhart/TensorFold/releases/tag/v1.2.8
|
What this changes
After each prompt chunk, Flash Next's MTP head runs over the chunk's rows to fill its own caches (
head.runfromprefillStepFn). It ran as a decode window (o.windowset,o.prefilloff,moe.run(..., false)), so its projections and its MoE took the stream tiles for up to a whole chunk of rows (4,096 with #1651), reading each weight once per four rows. A kernel trace of a cold 7,869-token prompt shows about 0.54 s ofaffine_dot2_streamlaunches with 928- to 4,096-row grids, one set per chunk.head.runnow takesprompt: true from the prompt fill, which setso.prefilland the MoE's prompt path and leaveso.windowoff; the decode windows pass false and keep their path. This is what the main layers already do with prompt rows.The head's caches after a prompt are not the same bytes as before: the prompt tiles' sums differ from the stream tiles' in the last bits, and the head's cache hash after a cold 7,869-token prompt differs from main's. This does not change a served token, because the target model verifies every draft: replies are equal to main's, and only the drafted tokens (and so the acceptance) can move. Measurements below.
atc overlap --jsonagainst main000b534a2: incomplete (pullFilesLimit,timeLimit). The change touchesCHANGELOG.mdandengine.zig,head.zigandlanes.zigunderzig/src/families/flashnext/hip/. hip flashnext: a shared round's streams draft in one head pass a step #2696 also changeshead.run's signature; whichever lands second needs a small update. hip flashnext: size the scratch arena by the dry run's peak, with the MTP head and a verify round's logits #2670 is in main, and its dry run now passesprompt = truehere, so it sizes the head's prompt path.Checks on this head
63914d6ae: the reviewed commit7169ea5ac, a merge of main000b534a2(conflict inCHANGELOG.mdonly),head.runtaking the prompt flag as an argument with hip flashnext: size the scratch arena by the dry run's peak, with the MTP head and a verify round's logits #2670's dry run passing true (6cd97ef66), and the changelog line (d885b0e71,63914d6ae). The receipt ran ond885b0e71; the head differs from it only in the changelog figure.python3 tools/zig/lean_check.py: 345 problems on the base and 345 on this head, 0 new findings.python3 tools/pr_check.py --base origin/main --pr 1878: 0 errors, 0 warnings.zig build test -Dhip=true --summary all: 889 of 891 steps succeeded, 1 test failed:dashboard_test"dashboard page polls the public stats snapshot without private details", which fails the same way on main000b534a2.zig build hip-kernel-teston gfx1151: 20 of 20 steps, 159 of 165 tests passed, 6 skipped, the same as main.Receipt
000b534a2againstd885b0e71. Zig 0.17.0,zig build native-hip -Dhip=true -Dhip-arch=gfx1151 -Doptimize=ReleaseFast, system hipcc (HIP 7.1, clang 21), Linux 7.0. One Strix Halo iGPU (Radeon 8060S, gfx1151), 128 GB unified memory. Checkpointunigilby/Qwen3.8-Flash-Next-oQ4e-HIPrevision5a0fb414, MTP 3 drafts,--context 131072 --parallel 4 --prompt-cache-gib 0. Arms in the order main, this, this, main, a fresh server with the page cache dropped for each; both arms of each side are shown.tools/bench_cache.py --require-cold, seconds: 2,048 tokens 4.19 / 4.07 on main vs 4.09 / 3.86 here (−3.7%); 7,377 tokens 12.28 / 12.28 vs 11.70 / 11.60 (−5.1%); 29,364 tokens 45.79 / 45.84 vs 42.98 / 43.31 (−5.8%); 58,728 tokens 92.48 / 92.90 vs 87.42 / 87.38 (−5.7%). Replytoken_sha256equal to main's at every size.tools/bench_concurrent.py --levels 1,2,4 --reps 3 --alone --serial --temperatures 1.0,0 --prompts tools/bench_prompts.json: every request'stoken_sha256equals main's in each arm (42 of 42 warm, 8 of 8 on a cold four-stream start); concurrent equals alone (3/3, 6/6, 12/12) and alone equals"draft": false(4/4) in every cell of every arm, 0 unequal, 0 failed.tools/bench_openai.py --tokens 256 --reps 3), code / chat: main 2.612 / 2.032 in both arms, here 2.612 / 2.065 in both arms. Earlier comments have the FP64 check of the head's two projections and the resumed multi-turn run.bench_openaicode 46.19 / 46.30 vs 46.14 / 46.17 tok/s (−0.2%, inside the spread between main's own arms), chat 40.03 / 40.12 vs 40.44 / 40.46.Checklist
"draft": false, a resumed prompt equals a fresh one, and concurrent equals solo. (Drafted and concurrent above, greedy and sampled; the resumed multi-turn run is in the comments, on the earlier head.)