Repository navigation
perf(qwen27): an accepted DeltaNet chain scans its state in place, no state publish copy - #711
Merged
Merged
Conversation
… state publish copy Each scan thread reads and writes only its own state words, so a chain round whose segments stay in their slots scans the state where it is and publishes only the conv history. Plain decode steps, verified draft chains and prompt chunks are all such rounds. A round whose slots move keeps the scan into the next state and the state publish copy.
jschmied
pushed a commit
to jschmied/TensorFold
that referenced
this pull request
Oct 13, 2026
Owner
|
Thank you, @jkuepker. Three careful speed changes to the 27B on Metal, each with its exactness argument written down, and all three hold on our machines: the in-place DeltaNet scan (#711), the decode attention without the tail kernel (#712), and the draft-side changes (#713). Your ten commits landed in 1.0.9 under your name, rebased onto 1.0.8 (only #713's first commit changed: three What we ran, with all three together against the tree they started from:
|
This was referenced Oct 13, 2026
Open
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.
In a chain round, the qwen27 DeltaNet pool scans each layer's recurrent state into the next-state buffer, then copies it back into the slot. Each of the 27B's 48 DeltaNet layers holds a 3 MiB fp32 state per slot (48 value heads × 128 × 128), so every decoded token read and wrote about 288 MiB just for that copy, in one extra dependent dispatch per layer.
Each scan thread reads and writes only its own state words. So when a round's segments keep their slots, the chain scan can run on the state where it is, and the publish step copies only the conv history. Plain decode steps, verified draft chains and prompt chunks are all such rounds:
round_planbuilds every runner plan withnext_slot == slot, including the multi-slot runners from #578. A plan whose slots move still takes the old scan-and-copy path, soforwardChainstays correct for any planvalidatePlanaccepts.The output bits don't change: the scan does the same arithmetic on the same inputs, and only its destination changes. #568 removed a similar copy in the qwen3_5 port before the 27B moved to this engine. This removes the copy in the qwen27 engine.
One file changes:
zig/src/families/qwen27/gdn_pool_gpu.zig, +19/−7. The round's alias check (Buffers.check) runs beforenext_stateis pointed atstate; the comment at that line gives the reason.Exactness
All on one Apple M5 Max, against origin/main
e7c523eb(1.0.7) built the same way.qwen27_gate.py check(4 prompts × 256 greedy tokens,--chunk 128 --compare-chunk 16)prompt_state_byte_equal4/4tf-qwen27-dflash-run --prototype q4 --compare-plain, DFlash2)qwen27_http_gate.py(raw/chat × plain/draft)bench_concurrent.py --levels 1,4 --alone --serial, code and chat)tf-qwen27-streams(real weights) at 4 and 8 streamstf-qwen27-synthetic,-lanes-check,-served-check,-dflash-served-check,-dflash-syntheticThe gate exits 1 on main too. Story (token 68) and code (token 21) leave the oracle at a BF16 near-tie: same two contenders, both margins within one BF16 step.
Speed
Same Mac and settings throughout, with no other model process running. Builds of origin/main
e7c523eband of this branch, run alternately (ABBA).Plain greedy decode (
tf-qwen27-run, 256 tokens, 5 interleaved pairs per context). A local patch adds per-step timing to both builds; it isn't part of this PR. Prompt, decode and token hashes are equal in every pair.At 16K both builds' spreads are about 1% and their ranges don't overlap.
Served (native server, 2 sessions per build, alternated main, e1, attn, attn, e1, main for plain and main, e1, attn, drafted, drafted, attn, e1, main for drafted). These show no change I can claim: every difference is inside main's own session-to-session spread.
tools/bench_openai.py --tokens 256 --reps 3 --temperatures 0,--no-drafts): fibonacci 29.72 → 29.74 tok/s, chat 29.20 → 29.80 (main's two sessions differ by 2.3% on chat).tools/zig/qwen27_served_bench.py, DFlash2 4-bit, 3 repeats): copy −0.1%, arithmetic +0.2%, story +0.6%, code −0.5%. Draft rounds and tokens per round are identical on every build (2.88, 8.53, 3.20 and 8.00).--prompt-cache-gib 0): 2k +0.3%, 8k −1.1%, 32k +2.5%; main's two 32k sessions are 0.5% apart but its within-session repeats span 4.5%, so no prefill change is claimed. 64k wasn't run.mlx_lm 0.32.0 (mlx 0.32.3), plain, on the same two prompts: 30.86 and 30.82 tok/s.
Host checks
zig build test test-golden -Dcpu=apple_m1: 143/143 steps, 1290/1292 tests passed, 0 failed, 2 skipped.test-golden: 309/319 equal, the 10 known differences, 0 unexpected. All the same as main.python3 tools/zig/lean_check.py: 534 problems on main and 534 on this branch, 0 new.zig fmt --checkis clean on the touched file.Setup: Apple M5 Max (40-core GPU, 128 GB), macOS 27.0 (26A428), Xcode 27.0, Zig 0.17.0. Checkpoint
TensorFold/Qwen3.8-27B-MLX-4bit@22d8d538; drafterz-lab/Qwen3.8-27B-DFlash2@50307d4c, 4-bit. Not run: CUDA and ROCm (Metal-only change), and Apple M1–M4.