[Spec] DSpark support prefill/decode disaggregation - #31466
Open
zhangxiaolei123456 wants to merge 104 commits into
Open
zhangxiaolei123456 wants to merge 104 commits into
zhangxiaolei123456 wants to merge 104 commits into
Conversation
|
@zhangxiaolei123456 I fix a problem when use dsv4-flash-dspark in PD mode, Is it related to your changes? #31513 |
Contributor
Author
|
@yz-tang this PR focus on DSpark hidden state transfer from prefill to decode, your PR may not have noticed this. |
hnyls2002
reviewed
Jul 20, 2026
hnyls2002
left a comment
Collaborator
There was a problem hiding this comment.
Seems like a lot of duplicated code hunks. Could you please get a roadmap for your PR? This should have a clear breakdown for each hunk of changes.
Collaborator
There was a problem hiding this comment.
Can you move this dspark logic out of scheduler.py
cursor Bot
pushed a commit
to learning-sketch/sglang
that referenced
this pull request
Jul 22, 2026
…(DSpark PD disaggregation) Summarizes sgl-project#31466: the problem (decode-side draft KV cannot bootstrap without target hidden states in PD mode), the design (PD_HIDDEN state type, decode-driven negotiation, PP-aware slicing, streaming transfer with ACK flow control and split hidden/KV completion semantics, registered GPU row pools), a layer-by-layer implementation walkthrough of all 26 changed files, compatibility boundaries, test coverage, and review status with engineering observations. Co-authored-by: learning <learning-sketch@users.noreply.github.com>
5 tasks
4 tasks
|
@zhangxiaolei123456 hello~ any plan to merge it? Looking forward to this fea! |
Contributor
Author
|
@zhangxiaolei123456 hi, can support split target layers across PP ranks? |
RyeYuan
added a commit
to HYGON-AI/sglang-das
that referenced
this pull request
Aug 14, 2026
Backport the final functional diff of sgl-project/sglang#31466 at c96e68b62 onto deepseek-v4-hcu while preserving HCU DCP, staging, CP, and graph-replay changes. PD runtime validation is pending dedicated prefill/decode resources. Static compilation and test_pd_hidden_state.py (12 tests) pass.
Chronostasys
pushed a commit
to MindLab-Research/sglang
that referenced
this pull request
Aug 15, 2026
…r + fix concurrent deadlock Root cause chain (4+ concurrent SIGQUIT crash): 1. New prebuilt batch's build_disagg_draft_input returned None (spec_info.py lacked the DSPARK branch) -> merge_batch skipped spec_info merge -> running batch kept stale bs=1 draft_input while batch grew to bs=N -> verify ForwardBatch mixed bs=1 input_ids with bs=N req_pool_indices -> cuda graph fill_from shape [1] vs [N] -> SIGQUIT all ranks. Fixes: - Port sgl-project#31466 core (16 files, ~2900 lines): PD_HIDDEN state type, hidden row pool, prefill-side capture, mooncake streaming transfer, decode-side inject_pd_hidden_chunk, DSPARK build_disagg_draft_input branch. - schedule_batch.merge_batch: guard 'if self.spec_info and other.spec_info' (None-safe; other.spec_info=None no longer crashes AttributeError). - fake/nixl/mori send_metadata: accept spec_metadata kwarg (signature compat with CommonKVReceiver, fixes warmup FakeKVReceiver TypeError). - eager/verify preload diagnostics kept behind exception-only paths (FILL-MISMATCH prints slot+shape on mismatch). Verified: single + 2/4/8/8 concurrent all-correct output, 0 scheduler crash, decode health 200 sustained.
RyeYuan
added a commit
to HYGON-AI/sglang-das
that referenced
this pull request
Aug 18, 2026
Backport the final functional diff of sgl-project/sglang#31466 at c96e68b62 onto deepseek-v4-hcu while preserving HCU DCP, staging, CP, and graph-replay changes. PD runtime validation is pending dedicated prefill/decode resources. Static compilation and test_pd_hidden_state.py (12 tests) pass.
Chronostasys
pushed a commit
to MindLab-Research/sglang
that referenced
this pull request
Aug 24, 2026
…r + fix concurrent deadlock Root cause chain (4+ concurrent SIGQUIT crash): 1. New prebuilt batch's build_disagg_draft_input returned None (spec_info.py lacked the DSPARK branch) -> merge_batch skipped spec_info merge -> running batch kept stale bs=1 draft_input while batch grew to bs=N -> verify ForwardBatch mixed bs=1 input_ids with bs=N req_pool_indices -> cuda graph fill_from shape [1] vs [N] -> SIGQUIT all ranks. Fixes: - Port sgl-project#31466 core (16 files, ~2900 lines): PD_HIDDEN state type, hidden row pool, prefill-side capture, mooncake streaming transfer, decode-side inject_pd_hidden_chunk, DSPARK build_disagg_draft_input branch. - schedule_batch.merge_batch: guard 'if self.spec_info and other.spec_info' (None-safe; other.spec_info=None no longer crashes AttributeError). - fake/nixl/mori send_metadata: accept spec_metadata kwarg (signature compat with CommonKVReceiver, fixes warmup FakeKVReceiver TypeError). - eager/verify preload diagnostics kept behind exception-only paths (FILL-MISMATCH prints slot+shape on mismatch). Verified: single + 2/4/8/8 concurrent all-correct output, 0 scheduler crash, decode health 200 sustained.
RyeYuan
added a commit
to RyeYuan/sglang-das
that referenced
this pull request
Aug 26, 2026
Backport the final functional diff of sgl-project/sglang#31466 at c96e68b62 onto deepseek-v4-hcu while preserving HCU DCP, staging, CP, and graph-replay changes. PD runtime validation is pending dedicated prefill/decode resources. Static compilation and test_pd_hidden_state.py (12 tests) pass. (cherry picked from commit d04b624)
RyeYuan
added a commit
to HYGON-AI/sglang-das
that referenced
this pull request
Aug 26, 2026
… v0.5.18 release. Based branch: feat/rye_20260814_deepseek-v4_open * [HCU][DSpark] Fix DP EP MoE routing on main (cherry picked from commit ac169c9) * DSpark support DeepEP & DeepGemm (cherry picked from commit 412dd6a638f1d1cc6ed162d40cfc4dfbb89915ea) (cherry picked from commit b781f71) * [HCU][DSpark] Backport PD hidden-state disaggregation Backport the final functional diff of sgl-project/sglang#31466 at c96e68b62 onto deepseek-v4-hcu while preserving HCU DCP, staging, CP, and graph-replay changes. PD runtime validation is pending dedicated prefill/decode resources. Static compilation and test_pd_hidden_state.py (12 tests) pass. (cherry picked from commit d04b624) * [HCU][DSpark] Fix DeepEP target and draft runtime state (cherry picked from commit ce62425) * [HCU][DSpark] Adapt PD initialization to current cache API (cherry picked from commit c00a0e4) * [BugFix][DeepSeek-V4-pd-disaggregation] fix the cp error in the disaggreation prefill phase (cherry picked from commit c8f44ba) * [FEAT] support PD disaggregation with DSpark as decode and cp+ep as prefill (cherry picked from commit 3aa5772) * [HCU][DSpark] Make PD hidden decode hooks tolerate unwired queues The PD hidden-state hooks added to DecodePreallocQueue.pop_preallocated and DecodeTransferQueue.pop_transferred dereferenced collaborators that are wired up after construction (transfer_queue, kv_manager) and per-request state that only exists once a request enters the PD hidden path (pd_hidden_state). Any caller holding a partially constructed queue -- including main's registered decode-queue cleanup tests -- hit AttributeError on the abort/failure paths. Guard the three sites: getattr for transfer_queue, a class-level kv_manager default on DecodeTransferQueue, and a None-tolerant pd_hidden_state read in _drain_pd_hidden_ready_chunks. Production wiring is unchanged (all three are always set by scheduler init), so this only affects the unwired case. Fixes the 3 test_decode_queue_cleanup failures that predate this rebase. (cherry picked from commit 45ea9b7) * [HCU][DSpark] Fold PD hidden-state init into Scheduler.init_disaggregation SchedulerDisaggregationInitMixin.__init_subclass__ swapped a full copy of init_disaggregation (SchedulerDisaggregationPrefillMixin, 175 lines) over the one defined in the Scheduler class body. That copy was forked from an older main and has since drifted: it lost the PD Decode DP-sync Gloo group, the rust-server ascend config store, the unified-memory disagg move gate, the get_disagg()/get_parallel() accessors, and -- the crash reported here -- `self.disagg_prefill_pending_chunk_rids`, which main's send_kv_chunk now maintains: File ".../disaggregation/prefill.py", line 2305, in send_kv_chunk self.disagg_prefill_pending_chunk_rids.discard(req.rid) AttributeError: 'Scheduler' object has no attribute 'disagg_prefill_pending_chunk_rids' Whole-method duplication cannot survive rebases: every main-side addition to init_disaggregation silently disappears on the PD path. The branch's actual delta is three lines wide -- resolve_disagg_metadata_config() widening the hidden-state geometry, plus **metadata_buffer_kwargs on the two MetadataBuffers constructions -- so fold exactly that into the single implementation, guarded on disaggregation_mode != NULL (a non-PD server must not inspect speculative workers), and delete the copy along with the installer mixin. Repro: P HCU_NUM=4 PD_OPEN=1 PD_MODE=prefill MTP_MODE=dspark DSPARK_MOE_MODE=deepep + D HCU_NUM=4 PD_OPEN=1 MTP_MODE=dspark DSPARK_MOE_MODE=deepep + router. (cherry picked from commit 1e4eeab) * [HCU] Fix PD Decode DP-sync block to use ParallelState accessors The PD Decode single-clock setup in init_disaggregation still reads self.pp_size / self.attn_tp_size / self.attn_cp_size / self.tp_rank, which no longer exist on Scheduler after the ParallelState (self.ps) refactor. The block only runs for disaggregation_mode == DECODE with dp attention, so nothing upstream exercises it and the stale names never surfaced: File ".../managers/scheduler.py", line 1315, in init_disaggregation if self.pp_size != 1: AttributeError: 'Scheduler' object has no attribute 'pp_size' Re-express the four reads on self.ps. Verified no bare self.{pp_size, attn_tp_size, attn_cp_size, tp_rank} remains in scheduler.py; the rest of the file already uses self.ps for all four. Inherited verbatim from the rebase base d20a475 -- it was reachable only once the duplicated init_disaggregation copy stopped shadowing the real one. Repro: D side HCU_NUM=4 PD_OPEN=1 PD_MODE=decode MTP_MODE=dspark DSPARK_MOE_MODE=deepep. (cherry picked from commit a511ebf) * [HCU] Use AITER TileLang MHC pre path (cherry picked from commit 22c5647) * [HCU] Add SlimQuant W4A8 Triton MoE backend (cherry picked from commit 60c48d0) * Fix PD hidden state transfer length (cherry picked from commit 4bde65d) * Support aiter dspark w4a8 (cherry picked from commit aa378d2) * perf(hcu): optimize DSV4 TopK transform with LightOp (cherry picked from commit a6617f2) * perf(hcu): run DSV4 FlashMLA with gathered BF16 KV (cherry picked from commit 790a529) * perf(hcu): add INT8 indexer cache for DeepSeek-V4 Persist C4 indexer K as signed INT8 with per-token FP32 scales, quantize and scatter cache rows with Triton, and route INT8 Q/K through the native LightOp Paged MQA path. Gate the feature with SGLANG_DSV4_HCU_INT8_INDEX_K_CACHE and keep DSpark draft workers on their existing cache path. Validated with a simple chat smoke test, a 128K BS=1 PD request, and full GSM8K (mean_acc=0.9689). (cherry picked from commit 505634b) * perf(hcu): use LightOp for DSV4 BF16 KV gather Add an environment-gated LightOp backend for the DSV4 single-cache and dual-cache gather/upconvert paths. Keep the existing Triton combined-cache implementation as the default fallback and validate the required LightOp symbols at startup. (cherry picked from commit 8b6e25f) * [BugFix][DeepSeek-V4-dspark] fix the compatible issues of deepep in dspark draft model due to a second LL Buffer (cherry picked from commit acae5ec) * fix(hcu): adapt _is_fake_transfer call to the release signature The PD hidden-state block cherry-picked from the DSV4 dev branch calls _is_fake_transfer(decode_req.req, self.scheduler.server_args) but this release line already refactored the helper to take the request only and read the transfer backend from get_disagg(); the two are semantically identical. On the release branch the stale call crashed the decode scheduler on the first preallocated batch: File ".../disaggregation/decode.py", line 1338, in pop_preallocated and not _is_fake_transfer( TypeError: _is_fake_transfer() takes 1 positional argument but 2 were given Neither py_compile nor ruff F821 sees an arity mismatch, so it only surfaced at runtime. A cross-tree sweep of every function whose arity differs between the release base and the dev branch (71 of them) over the cherry-picked files found no other call site that fails to fit the release signature. * w4a8 draft moe backend triton (cherry picked from commit 79c0fb1) * [docs] remove unused test unit test cases and relocate docs --------- Co-authored-by: elmo2019 <elmo2019@163.com> Co-authored-by: Yujiang Chao <chaoyujiang@hygon.cn> Co-authored-by: aivictor0901-tech <aivictor0901@gmail.com> Co-authored-by: chaoyujiang <chaoyujiang@hygon.com>
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.
Motivation
Roadmap: #30344
Reference PR: #30513
This PR adds DSpark support for prefill/decode disaggregation on DeepSeek-V4.
On
main, DSpark can run in the normal non-disaggregated path, but the PD decode path does not receive the target-model hidden states required to bootstrap the draft-side DSpark state. As a result, decode may enter speculative decoding without valid DSparkspec_info/ draft KV state.This PR fixes that gap by transferring DSpark target hidden states from Prefill to Decode through the disaggregation protocol, then injecting them into the decode-side draft KV cache before the first draft step.
The implementation keeps the existing non-disaggregated DSpark behavior unchanged and scopes the new behavior to PD mode.
What Changed Compared With main
DSpark Hidden State PD Transfer
DSPARK_HIDDEN.Decode-Side Draft Bootstrap
prefill_tail_hidden_states.PP-Aware Prefill Support
Hidden Buffer and Transfer Resource Management
DSparkHiddenTransferPlanto describe row/chunk transfer layouts.DSparkHiddenPagePoolto reuse registered GPU receive buffers.SGLANG_DSPARK_PD_HIDDEN_RECV_PREWARM_ROWSSGLANG_DSPARK_PD_HIDDEN_RECV_PREWARM_PAGEStorch.emptyinstead oftorch.zeroswhen assembling Decode hidden tensors to avoid unnecessary clearing.Metadata and Speculative Decode Integration
DFlashDraftInputV2/ DSpark draft input to carry:prefill_tail_hidden_statesprefill_tail_valid_maskprefill_tail_start_positionsprefill_tail_hidden_projectedStreaming Hidden Chunk Transfer and Release Semantics
prompt_len * hidden_widthtensor on Prefill or Decode.dspark_hidden_current_*slot.Why These Changes Are Needed
DSpark speculative decoding depends on target-model hidden states to initialize the draft-side state correctly. In PD mode, Prefill and Decode run in separate processes, so Decode cannot derive those hidden states locally.
The new
DSPARK_HIDDENtransfer path makes the hidden state an explicit part of the PD protocol, similar to KV/state transfer, but with PP-aware slicing because only specific Prefill PP ranks own the DSpark target layers.The resource probing and transactional allocation logic are needed to avoid PP rank divergence: all PP ranks must agree on which requests enter the pipeline before any rank performs side-effectful resource allocation.
The dynamic registered GPU receive buffer pool avoids CPU bounce and keeps hidden transfer on the GDR path.
Compatibility
DSPARK_HIDDENmetadata.Current Scope and Future Work
This PR supports DSpark PD hidden-state transfer for Prefill with PP. The implementation is PP-aware: each Prefill PP rank only captures and transfers the DSpark target hidden slice owned by its local layer range.
Mooncake now supports streaming DSpark hidden transfer with hidden/KV/request completion semantics separated. NIXL and MORI do not yet implement streaming hidden release semantics and therefore keep the default non-streaming capability boundary.
Prefill radix cache cannot be enabled alone for DSpark PD today.
DSpark hidden transfer must match the KV transfer window. If Prefill radix cache is enabled but Decode radix cache is disabled, Prefill may skip cached prompt tokens and the corresponding target hidden states will be missing on Decode.
Therefore, DSpark PD currently requires Prefill and Decode radix-cache policies to be consistent. Until DeepSeek-V4 Decode radix cache is supported, Prefill radix cache should stay disabled. #31097
Future work will extend this to Prefill with PP + TP + CP and streaming support for NIXL/MORI. That requires generalizing the hidden metadata from PP-only layer slicing to a full PP/TP/CP layout, including tensor-parallel hidden sharding and context-parallel token-range reconstruction on Decode.
Accuracy Tests
Speed Tests and Profiling
Checklist
Review and Merge Process
/tag-and-rerun-ci,/tag-run-ci-label,/rerun-failed-ciCI States
Latest PR Test (Base): ❌ Run #29816426168
Latest PR Test (Extra): ❌ Run #29816426138