speculative : capped recurrent planes for DFlash (--spec-draft-rs-planes) - #91
Open
Piggidragon wants to merge 4 commits into
Open
Piggidragon wants to merge 4 commits into
Piggidragon wants to merge 4 commits into
Conversation
Keep --spec-mtp-rs-planes as an alias. Rename the capped-replay helpers that are not specific to MTP. Assisted-by: Claude Opus 5
DFlash keeps its draft state in the draft KV cache, which the capped replay already rewinds with seq_rm, so it has no replay state to save. The planes stay owned by a single speculative mode. Assisted-by: Claude Opus 5
Piggidragon
force-pushed
the
spec/draft-rs-planes-dflash
branch
from
September 14, 2026 21:54
137ab0a to
5844ed2
Compare
This was referenced Sep 17, 2026
The reserved-input mode only kept the states of the last tokens. Add leading_snapshots, which keeps the states after the first tokens in the slots below the input slot, so a caller can pick where the snapshots are. Assisted-by: Claude Opus 5
The capped planes kept the newest states, which suits MTP. DFlash rejects most drafts early, so it replayed 76% of the verifications. --spec-draft-rs-planes-lead sets how many planes keep the states after the first draft tokens. One plane still keeps the newest state, which a fully accepted draft continues from. The default picks the layout per mode: all but one leading plane for DFlash, none for MTP. With capped planes a rollback without replay now always selects a kept plane, instead of falling back to the checkpoint restore. Assisted-by: Claude Opus 5
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.
Lets DFlash use the capped recurrent planes that
draft-mtpalready has, renames the flag to match, and changes which states the capped planes keep.What changes
--spec-mtp-rs-planesbecomes--spec-draft-rs-planes. The old name is still accepted, and the env var is nowLLAMA_ARG_SPEC_DRAFT_RS_PLANES.draft-dflashcan cap the target recurrent planes belowspec-draft-n-max + 1. A rollback to a kept state selects its plane directly; any other one restores the pre-verification plane and replays the verification batch on the GPU, the same path MTP uses.seq_rm. The only DFlash-specific code is a replay-state hook that saves nothing.--spec-draft-rs-planes-lead Nsets how many planes keep the states after the first draft tokens. One plane always keeps the newest state, which a fully accepted draft continues from. The default picks the layout per mode, so MTP keeps its current behavior.ggml_gated_delta_nettakes aleading_snapshotsparameter for this. It is implemented in the CPU, CUDA and Vulkan kernels and in the CUDA cache fusion.Why the layout matters
The capped planes used to keep the states after the last tokens of the verification batch. That suits MTP, which accepts most of its draft. DFlash rejects early, so the states it needs were exactly the ones not kept.
Share of verifications by accepted draft count, uncapped,
n-max 7, 1422 verifications:Full acceptance is a second peak, which is why one plane stays on the newest state. With 4 planes the old layout covers accepted counts 5, 6 and 7 (23%), the new one covers 0, 1 and 7 (59%).
Results
Qwen3.8-27B-UD-Q4_K_XL + DFlash2-Q4_K_M over RTX 4070 + RTX 3060, host KV cache,
parallel 2,n-max 7, 131k per slot, except where noted. Measured on a local merge of this PR with the open PRs this setup needs. "old" islead 0, the layout before this PR; "new" is the default DFlash layout. Decode in t/s.Correctness. Under tensor split and under layer split, single-request greedy generations with 4 and 5 planes, in both layouts, are byte-identical to the uncapped run and accept the same draft tokens. Two concurrent requests are not byte-identical to the uncapped run in any capped layout; see "Not covered".
Tensor split.
Layer split.
Single GPU, RTX 4070, Qwen3.8-27B-UD-IQ2_M + DFlash2-Q2_K,
parallel 1, 65k context.Uncapped does not fit on one 12 GB card: 9228 MiB of weights plus 1197 MiB of planes leave too little, and the run dies allocating a 182 MiB compute workspace. There the cap is not a trade, it is what makes the configuration possible, and the new layout is 26% faster than the old one at the same peak VRAM. The two layouts produce identical output and identical acceptance there.
Each plane holds one recurrent state per sequence, about 150 MiB on this model, so the 4-plane runs hold 1197 MiB of recurrent state instead of 2394 MiB with two slots.
Prefill is unchanged in every run: 495-499 t/s tensor split, 536-544 t/s layer split, 903-904 t/s single GPU.
The decode cost tracks the replay share: a replay is a second target decode of the verification batch. With the new layout, 4 planes go from 54% to 68% of uncapped decode under tensor split, and 3 planes beat the old 4-plane layout while saving another 170 MiB per GPU.
Not covered
AI assistance: the implementation, tests and measurements were done with Claude Opus 5; each commit carries
Assisted-by:.🤖 Generated with Claude Code
https://claude.ai/code/session_01DPWnbaJURJQJbbQv6e4D8y