Repository navigation
perf(qwen): full-context MTP draft prefill and replay-free rewind - #14
Conversation
Fix and speed up Qwen native MTP speculative decoding: - Prefill the MTP draft layer's attention cache over the whole prompt after layer-major prefill. It previously received only the last mtp_slots // top_k positions (3-4 tokens at default budgets), which cut draft acceptance (Code: 65% -> 81%). - Run that draft-layer prefill through one whole-layer expert buffer instead of slot-sized chunks. - Stop disabling grouped expert prefill when MTP is enabled; grouping only reorders gather_qmm rows and is bit-identical. Update the App toggle and its localized hints accordingly. - Roll back rejected drafts without replaying accepted tokens through the MoE: trim verified attention caches and rerun only PLE and the Gated DeltaNet mixer on inputs captured during verification. - Truncate pooled QSA index keys on trim instead of rebuilding them. M4 Max 36 GB, 4096-in/256-out greedy, interleaved A/B (2 runs each): MTP decode 7.40 -> 9.88 tok/s on Code (+33%), 5.78 -> 8.32 on Novel (+44%); Code MTP TTFT 25.2 -> 21.5 s. MTP-off output and speed are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@alan13367 |
You're welcome, thanks for the awesome work in this project! I'll keep using it and contributing if I see good opportunities to do so :D |
Summary
Qwen native MTP was a net loss on SSD-offloaded decode. This PR fixes why and makes it faster:
mtp_slots // top_kpositions (3–4 tokens at default budgets). It is now filled from the whole prompt. On Code, draft acceptance rose from 65% to 81%.batched_layer) inprefill_step_sizechunks, instead of many slot-sized chunks.gather_qmmrows, and the existing bit-identity test covers it. The App toggle and its hint text (en / zh-Hans / zh-Hant) no longer say "requires MTP off".triminstead of rebuilt; complete blocks before the new offset are unchanged.The installed model format, sampling, and the MTP-off path are unchanged.
Measurements
Apple M4 Max, 36 GB, macOS 27.0.1, MLX 0.32.2. I ran the runtime directly, not through the App or HTTP server. Each run is one request in a fresh process: bundled Code or Novel corpus, 4,096 input tokens, 256 output tokens, greedy seed 42, Prompt Cache off.
Settings: 7.5 GiB expert cache, LFU, 16 read workers, 1,024-token batched layer-major prefill. MTP runs use a 1.3 GiB MTP cache, 2 draft tokens, and a zero-acceptance limit of 32. Base
ba9e126and this branch were interleaved A/B/B/A, two runs per arm. The OS page cache was not purged.Caveats
BENCHMARK.mdhas the full section, including a rejected decode-time next-layer expert prefetch. It kept outputs identical but was slower, because this SSD is bandwidth-bound: one 2.6 MB expert read takes about 0.65 ms, and four concurrent reads take about 1.95 ms.Tests
runtime/tests/test_qwen_mtp_rewind.py: a tiny 4-layer Qwen with PLE, with and without pooled index keys. It checks rewound caches and next-token logits against a direct forward over the accepted prefix, and asserts no routed experts are used. I confirmed it fails if the PLE rerun or the KV trim is removed.make test-python: 618 tests, 0 failures, 10 optional skips.make test: 147 App + 26 repack tests, 0 failures, 4 optional skips.🤖 Generated with Claude Code