[None][feat] Page the DSpark drafter context through the draft KV cache manager - #18343
Draft
dc3671 wants to merge 1 commit into
Draft
[None][feat] Page the DSpark drafter context through the draft KV cache manager#18343dc3671 wants to merge 1 commit into
dc3671 wants to merge 1 commit into
Conversation
dc3671
force-pushed
the
user/zhenhuanc/dspark-kvmgr
branch
3 times, most recently
from
August 28, 2026 09:37
ab3b1c0 to
6f5df55
Compare
1 task
…he manager The standalone drafter kept a private context arena, dense in max_seq_len and allocated after the KV pool had been carved, so free_gpu_memory_fraction never bounded it: at max_seq_len 997696 with max_batch 8 it wants 21.4 GiB/rank and the worker cannot start. Read the already-funded draft pool through the manager's per-request block tables instead, so the footprint follows the sequences served, and size what remains from max_seq_len rather than the drafter's advertised max_position_embeddings (1048576 for K3, hundreds of GiB on its own). The pool index space differs between the V1 and V2 managers, so the block-table divisor is derived from stride(0) rather than assumed. Three gates kept an external drafter off that path, each written for a mode it does not describe: - attention DP: the bail suits MTP, whose draft layers are target-shaped and appendable to the target pool. An external drafter has its own architecture, so nothing is appended and it stayed on the arena -- which under attention DP is sized with KV heads unsharded (20480 vs 2560 B/token for K3). - disaggregation: nvbugs/5807902 reported an Eagle3 RMSNorm failure and was worked around by disabling the separate draft KV cache for every speculative mode. Keep the workaround where it was reported. - disagg slot allocation: _store_prefill_context was the only place that assigned a drafter slot, so a generation worker -- which receives prompt KV instead of prefilling -- collapsed every concurrent request onto the single dummy slot. A context-only worker also releases the target's IndexMapper slot after prefill; the draft mirror never got that call and saturated. Accuracy cannot detect a broken drafter: speculative decoding is lossless, so one producing garbage scores the same and only runs slower. The added test therefore asserts on acceptance length. That is not hypothetical -- the slot collapse showed up as AL 1.087 vs 3.441 (decode steps 123860 -> 39072 for the same output length) while every accuracy gate passed. Signed-off-by: Zhenhuan Chen <zhenhuanc@nvidia.com>
dc3671
force-pushed
the
user/zhenhuanc/dspark-kvmgr
branch
from
August 31, 2026 07:37
6f5df55 to
a0ef415
Compare
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.
Follow-up to #18043 (merged); rebased onto
mainat 891b483.What
The standalone DSpark/DFlash drafter keeps its context KV in a private
arena. That arena is dense in
max_seq_len, allocated after the KV pool hasbeen carved (so
free_gpu_memory_fractionnever bounds it), and sized from thedrafter's advertised
max_position_embeddingsrather than the runtime limit.For Kimi K3 that is 1048576, hundreds of GiB on its own; at
max_seq_len997696 with
max_batch_size8 the arena wants 21.4 GiB/rank and the workercannot start.
This reads the already-funded draft pool through the manager's per-request
block tables, so the drafter's footprint follows the sequences actually served.
max_seq_lenmax_position_embeddingsmax_seq_lenNote the pool index space differs between the V1 and V2 managers — V1 returns a
per-layer strided slice, V2 a dense view over the layer-interleaved page space —
so the block-table divisor is derived from
stride(0)rather than assumed.Getting this wrong is silent: AL drops ~20% with no error and no accuracy
change.
Three gates, each written for a mode it does not describe
and appendable to the target pool. An external drafter has its own
architecture and layer count, so nothing gets appended and it stayed on the
arena — which under attention DP is sized with KV heads unsharded
(20480 vs 2560 B/token for K3).
disagg and was worked around by disabling the separate draft KV cache for
every speculative mode. The root cause was never found and the bug closed
will-not-fix, so the workaround stays where it was reported.
_store_prefill_contextwas the only placeassigning a drafter slot, so a generation worker (receives prompt KV, never
prefills) collapsed every concurrent request onto the single dummy slot. A
context-only worker also releases the target's
IndexMapperslot onceprefill finishes; the draft mirror never got that call and saturated until
copy_batch_block_offsets()asserted.Why the test asserts on acceptance length
Speculative decoding is lossless, so a drafter producing garbage scores the
same on GSM8K and merely runs slower. Accuracy cannot detect a broken
drafter. This is not hypothetical — it is how the slot collapse above was
found:
Output length matched to within 0.1% (134575 vs 134463 tokens), so the 3.2×
step reduction is pure acceptance. AL 1.087 means 1.2% of 7 draft tokens were
accepted — speculative decoding was effectively dead on that path, and every
accuracy gate still passed.
Validation
GB300, K3 DSpark unless noted.
max_seq_lenAlso exercised against the embedded DSpark path (DeepSeek-V4-Pro, DEP4
cutedsl, AL 4.436) — a different drafter shape over the same shared
kv_cache_manager_v2/py_executorcode this touches.The added test itself was run on this branch's base, 8x GB300, all 1,319
samples: GSM8K 96.361 (threshold 93.297) and AL 4.355, in 25m45s.
On AL references: the 5.2-5.5 figures above are
--apply_chat_templateruns,which is worth ~40% acceptance on GSM8K (3.81 plain vs 5.52-5.88 chat, same
drafter, same 1,319 samples). Sample count is not the variable — 200 and 1,319
agree within 5% at fixed prompt form. The stock lm-eval workload this test uses
measured 4.16 on the same path before this change, so
min_alis set at 4.0:a return to the old behaviour still passes, an acceptance collapse (1.09, and
every accuracy gate green) does not.
CI placement
TestKimiK3::test_gsm8k_tep8_dsparkneeds 8 GPUs and GB300-class memory (TP8leaves ~182 GiB of weights per GPU before any KV cache, past what a 184 GiB
B200/GB200 part holds), so it is registered as a 2x4 GB300 post-merge stage
following #18363: a new
l0_gb300_multi_nodes_node2_gpu8.ymlplus aGB300-8_GPUs-2_Nodes-1_NVLink_Domain-PyTorch-Post-Merge-1entry inmultiNodesSBSAConfigs. The1_NVLink_Domainname is load-bearing — it iswhat forces
--segment=2, and a K3 instance straddling two NVL domains dies insymm_mem.rendezvouswithCUDA driver error: invalid resource handle.Overlaps #18363, which is not merged yet: the
singleNvlinkDomainModeplumbing in
L0_Test.groovyand thel0_gb300_multi_nodes_node2_gpu8.ymlheader are the same hunks. Whichever lands second should drop the duplicate and
keep only its own test line / stage entry. Carried here rather than stacked so
this PR is correct standalone — without
--segmentthe stage can silentlyallocate across racks.
The drafter checkpoint is on the CI model share as
Kimi-K3-DSpark(llm-models!595, merged; RadixArk/Kimi-K3-DSpark @ 3c5bac3).
Not covered
The VANILLA drafter attention backend still uses the contiguous
flash_attn_with_kvcacheand is untouched; only the TRTLLM backend reads thepaged pool.