Repository navigation
[ROCm] Support decode context parallel (DCP) for GLM-5 / DeepSeek-V3.2 - #42618
Draft
EricKing626 wants to merge 9 commits into
Draft
EricKing626 wants to merge 9 commits into
EricKing626 wants to merge 9 commits into
Conversation
EricKing626
force-pushed
the
amd/dcp-glm-dsa
branch
from
October 7, 2026 07:37
8a07e5d to
42cb8a8
Compare
…k-V3.2 The DSA backend had no DCP path, so DSA models could not shard the KV cache across the DCP group. Reuse the existing DCP plumbing (widened allocator owner rule, Q all-gather, LSE merge) and add the DSA pieces: - Writes: MLA fp8 KV and indexer K only land on the owner rank. - Indexer decode: score the local index-K shard, take a local top-k and all-gather (score, global position) candidates to merge the global top-k. - Indexer prefill: rebuild the full-sequence index K from the shards. - Sparse decode: keep the owned top-k slots, attend with mla_gluon_decode and return the natural-log LSE for the cross-rank merge. - Sparse prefill: attend the all-gathered dcp_kv_buffer. - Disable fused top-k, the tilelang fused rope+cache path and MHA one-shot prefill under DCP; they assume the full KV is local. Scope: ROCm, qlen=1 decode; speculative decoding, hisparse and index kpool are rejected for now.
…ange, custom-AG LSE merge - Sparse decode under DCP uses the Triton split-K kernel instead of mla_gluon (mla_gluon asserts min_kv_seq_len > 784 and DCP shards are short). The reduce kernel optionally writes the natural-log LSE (STORE_LSE). - Owned slots are compacted to the front (dcp_compact_owned_slots) and the split kernel bounds its tiles by the per-row owned count. - dcp_exchange_topk: fused pack / merge Triton kernels; the global merge uses the DSA topk_func instead of torch.topk; gather as fp32 so ROCm takes the custom all-gather. - dcp_a2a_lse_reduce: on ROCm use the custom all-gather instead of RCCL a2a. - Add test/registered/dcp/test_dcp_dsa_unit.py.
EricKing626
force-pushed
the
amd/dcp-glm-dsa
branch
from
October 7, 2026 10:16
42cb8a8 to
ab07236
Compare
…exer, owned top-k + LSE merge DCP prefill used to gather the whole KV and run absorbed MLA on every rank. Three changes, chosen per batch: - Short prompts (kv_len <= SGLANG_DSA_PREFILL_DENSE_ATTN_KV_LEN_THRESHOLD) take the MHA one-shot path under DCP too; the prefix comes through all_gather_kv_cache_for_mha_extend, which dequantizes the fp8 cache. Absorbed MLA costs ~3.4x the MHA FLOPs per q.k pair, which made short-context TTFT ~3x TP4. - The gathered-KV indexer splits query rows across ranks once the mean key length per query token reaches SGLANG_DCP_DSA_SPLIT_INDEXER_MIN_KV (default 8192): each rank scores 1/W of the rows against the all-gathered index K and the int32 top-k is all-gathered. Shorter batches score every row on every rank. - When the cached prefix is long next to the extend (SGLANG_DCP_DSA_OWNED_PREFILL_RATIO, default 3.0 x gathered heads x extend tokens), prefill runs like decode: each rank scores its own index-K shard, the top-k candidates go through the same exchange and merge as decode (global positions, identical on every rank), and attention reads only this rank's owned slots with an LSE merge (forward_batch.dcp_owned_prefill), so the prefix KV is never gathered. GLM-5.2 MXFP4, TP4 + DCP4 on MI355X: GSM8K 0.933, passkey 9/9 (8K-76K); with every prefill forced onto the owned path (ratio 0, dense threshold 0) GSM8K 0.924, passkey 9/9. Short extend on a cached 16K-128K prefix: 182-308 ms TTFT.
… target; HiCache with MTP DSA + DCP rejected speculative decoding. Chain EAGLE/NEXTN (--speculative-eagle-topk 1) now runs with the draft inheriting the target's DCP layout, as in vLLM: - The DSA draft KV pool shards like the target (KVCacheConfigurator.loc_space_scale is 1 for it), so it is 1/W the size of a replicated draft and its writes take the owner rule. - Draft prefill, draft decode and their CUDA graphs reuse the DCP prefill and decode paths. TARGET_VERIFY and DRAFT_EXTEND_V2 rows are one query per draft token, so the indexer exchanges top-k candidates and forward_extend attends this rank's owned slots and LSE-merges them like decode (is_dcp_mla_decode_phase includes DRAFT_EXTEND_V2). - The draft runner's extend and multi-step backends are bound to its KVIndexTranslator; the DCP planner reads it through get_attn_backend(). - MTP IndexShare (index_share_for_mtp_iteration) is off under DCP: each rank holds only its owned share of the top-k. - HiCache + DCP accepts EAGLE/NEXTN on DSA models; the draft packs into the target's host rows. The DSA indexer host pool pages the widened transfer indices (logical_page_size), which previously addressed index-K pages W times too far and faulted once slots passed the per-rank capacity.
4 tasks done
The aiter custom all-gather moves W x the bytes the merge needs; it only wins while the exchange is latency bound. Above SGLANG_ROCM_DCP_LSE_AG_MAX_BYTES (default 1 MiB per rank) use the byte all-to-all, which is 2-3x faster at bs 32-64 verify sizes.
Drop SGLANG_ROCM_DCP_LSE_AG_MAX_BYTES and the ROCm custom all-gather branch; the LSE merge now always uses the byte all-to-all, which moves 1/W of the bytes the all-gather did.
…ens / tables DcpStep localizes out_cache_loc once per forward (target and draft) and memoizes the per-rank lens and index block tables, instead of recomputing them in every layer.
With kv_splits == 1 and return_lse the split partial already is the final result; write it and the natural-log LSE directly instead of round-tripping through the split-K workspace and the reduce kernel.
Gate the shared-file changes on DSA so dense MLA + DCP behaves as before: - is_dcp_mla_decode_phase takes use_dsa; draft extend and owned prefill count as the decode phase only for DSA attention. - The EAGLE v2 draft binds its swapped-in backends to the KV index translator only for a DSA draft under DCP. - dcp_forward_scope opens only for DSA models (target and NextN draft).
This branch has not been deployed
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.
[DSA][ROCm] Support decode context parallel (DCP) for GLM-5 / DeepSeek-V3.2
Motivation
Under pure TP, every rank of an MLA model stores a full copy of the KV cache. Decode context parallel (
--dcp-size) shards tokens across ranks to remove that duplication. SGLang already supports DCP for dense MLA, but not for DSA models: GLM-5 and DeepSeek-V3.2 run on thedsabackend, which has no DCP path.DSA picks the top 2048 tokens of the whole sequence before attention. Under DCP this adds three problems:
With this PR, each rank stores 1/W of both caches, in decode, prefill, MTP speculative decoding and HiCache. The algorithm follows vLLM's DCP for DeepSeek-V3.2 (vllm#58985), including a draft model that inherits the target's DCP layout, and reuses SGLang's existing DCP plumbing.
Modifications
The DSA-specific logic is in a new
layers/dcp/dsa.pyand a few Triton kernels inkernels/ops/attention/dcp_kernels.py. Other files get small hooks.slot % W == rank).kv_len <= SGLANG_DSA_PREFILL_DENSE_ATTN_KV_LEN_THRESHOLD) use MHA one-shot on the all-gathered, dequantized prefix.SGLANG_DCP_DSA_SPLIT_INDEXER_MIN_KV(8192), each rank scores 1/W of the query rows and the int32 top-k is all-gathered.SGLANG_DCP_DSA_OWNED_PREFILL_RATIO, 3.0 × gathered heads × extend tokens), prefill runs like decode: owned top-k plus LSE merge, and the prefix KV is never gathered.--speculative-eagle-topk 1): the draft shards its KV like the target, so its pool is 1/W the size of a replicated draft. Draft prefill, draft decode and their CUDA graphs reuse the DCP prefill and decode paths. Target verify and draft extend run one query per draft token through the owned top-k plus LSE merge path. MTP IndexShare is off under DCP, because each rank holds only its owned share of the top-k.test/registered/dcp/test_dcp_dsa_unit.py,test/registered/dcp/test_dcp_layout_unit.py.Supported scope: ROCm,
interleave_size=1, HiCache without an L3 storage backend. Rejected for now:--speculative-eagle-topk > 1, other speculative algorithms,--enable-hisparseand index kpool.Accuracy Tests
GLM-5.2-MXFP4 on MI355X, fp8 KV cache. MTP uses 5 steps and 6 draft tokens.
Speed Tests and Profiling
Same setup,
--max-running-requests 128, random prompts of fixed length, output length 256. TPUT per GPU = output tokens/s ÷ 4. Interactivity = 1000 / TPOT. TTFT, TPOT and ITL are medians.Summary:
Long context, long output, 128 concurrent requests (no MTP,
--max-running-requests 128, CUDA graphs up to bs=128, 128 prompts at concurrency 128, fixed lengths). Peak running requests and KV usage are sampled from/metricsevery 5 s. The TP4 rows come from an earlier session with the same server arguments.Future work
dcp_localize_write_loc(the owner-rulewhereon every MLA KV and index-K write). Computing the local write location once per step and fusing it into the write kernels is the cheapest next win.w_kcon gfx950 (--dcp-replicate-q-proj), and projecting V before the LSE merge. In earlier measurements they cut decode ITL by up to 4% and 9% at c=64.Checklist
CI States
Latest PR Test (Base): Not run yet⚠️ Not enabled -- add
Latest PR Test (Extra):
run-ci-extralabel to opt in.Latest PR Test (AMD ROCm 10): ➖ No AMD PR run found for this commit.