server : keep speculative recurrent-state checkpoints on-device - #28118
server : keep speculative recurrent-state checkpoints on-device#28118vahpetr wants to merge 1 commit into
Conversation
For recurrent/hybrid models whose target context is SEQ_RM_TYPE_FULL (e.g. Gated DeltaNet / Mamba hybrids, Qwen3-Next / qwen4exp), speculative decoding must checkpoint and restore the full recurrent state every round. The server took those per-round snapshots with LLAMA_STATE_SEQ_FLAGS_PARTIAL_ONLY, which serializes the whole state to host — on AMD Strix Halo (gfx1151) this was ~600 ms of each ~825 ms round, a constant ~73% overhead that makes speculative decoding a net loss despite high draft acceptance. OR in LLAMA_STATE_SEQ_FLAGS_ON_DEVICE at the eight live spec_ckpt calls (update_tgt/update_dft/load_tgt/load_dft) so the transient speculative snapshots stay on-device. The prompt-history checkpoint (cur.update_* near update_pos) and the disk/prompt-cache path (prompt_save / llama_state_seq_save_file) keep host serialization, since ON_DEVICE buffers are transient and host-inaccessible. Measured on AMD Strix Halo gfx1151 / Vulkan+RADV, Qwen3.8-Flash-Next Q4_K_M, -ctk q8_0 -ctv q8_0, temp 0, -np 1, MTP draft: no draft: 32.4 t/s spec before (host checkpoint): 6.2 t/s (5x LOSS) spec after (this change), n=3: 41.5 t/s (+28%, accept 0.79) spec after, code, n=6: 56.6 t/s (accept 0.91) Greedy output stays equivalent to no-draft (both diverge only through the backend's own non-deterministic reductions). Note: llama-memory-recurrent hard-aborts if cell_ranges.size() > 1 under ON_DEVICE; -np 1 yields one contiguous range, but fragmentation / cache-wrap paths should be guarded or fall back to host checkpointing.
|
Hi @vahpetr, thanks for your contribution! Per our contribution guidelines, the automated PR checker found the following issue(s) that need your attention:
Please note that maintainers reserve the right to make final decisions on PRs. If you believe there is a mistake, please comment below. |
|
A better fix would be to create such draft models with |
|
Thanks, you're right. qwen3next and qwen4exp just weren't in Measured on a Strix Halo (gfx1151, Vulkan), MTP draft, temp 0,
Output stays the same as the no-draft run. Opened #28120 for the 2-line change. |
|
Opened #28123 for the qwen4exp side of this, it removes the checkpoint instead of moving it on device. MTP head and draft patch here: https://huggingface.co/dzannotti/Qwen3.8-Flash-Next-MTP-GGUF/ Your change is still worth pursuing for the recurrent architectures that have no rollback support yet, and for the spec types that leave n_rs_seq at zero, where the checkpoint stays on the hot path. The guard you mention in the caveat looks like the blocker to me though: llama-memory-recurrent.cpp:824 aborts the process rather than returning an error, so as written the flag turns a slowdown into a crash as soon as a sequence has more than one cell range. |
|
This |
Summary
For recurrent / hybrid models whose target context is
SEQ_RM_TYPE_FULL(Gated DeltaNet / Mamba-style hybrids, e.g. Qwen3-Next / qwen4exp), speculative decoding must checkpoint and restore the full recurrent state every round. The server takes those per-round snapshots withLLAMA_STATE_SEQ_FLAGS_PARTIAL_ONLY, which serializes the whole state to host memory.On AMD Strix Halo (gfx1151) this host round-trip is ~600 ms of each ~825 ms round — a constant ~73 % overhead that makes speculative decoding a net loss despite high draft acceptance.
Change
OR in
LLAMA_STATE_SEQ_FLAGS_ON_DEVICEat the 8 livespec_ckptcalls (update_tgt/update_dft/load_tgt/load_dft) so the transient per-round speculative snapshots stay on-device. Left host-backed on purpose:cur.update_*next toupdate_pos) — a longer-lived snapshot that a later one would invalidate, andprompt_save,llama_state_seq_save_file) — needs host-accessible bytes.8 lines, no API change; the library already supports the flag.
Measurements
AMD Strix Halo gfx1151 / Vulkan + RADV, Qwen3.8-Flash-Next Q4_K_M,
-ctk q8_0 -ctv q8_0, temp 0,-np 1, MTP draft head:Greedy output stays equivalent to no-draft (both diverge only through the backend's own non-deterministic parallel reductions — two no-draft runs already differ at temp 0).
Caveat for review
llama-memory-recurrentcurrently hard-aborts ifcell_ranges.size() > 1underON_DEVICE.-np 1yields one contiguous range, but fragmentation / recurrent-cache-wrap / truncation should be guarded (a controlled error, or a fall back to host checkpointing) rather than aborting. Happy to add a guard + a small regression test on the update→load invalidation semantics if maintainers prefer.Root cause was first diagnosed by @JayToltTech in the qwen4exp MTP thread (#27836); this PR is the standalone server change (the touched code is already in
master) plus independent gfx1151 measurements.