Skip to content

[Feat] kimi-k3.1: DSPARK and DFLASH under decode context parallelism - #48739

Open
kpham-sgl wants to merge 31 commits into
sgl-project:mainfrom
kpham-sgl:kpham/k31-dcp-spec-verify
Open

kpham-sgl wants to merge 31 commits into
sgl-project:mainfrom
kpham-sgl:kpham/k31-dcp-spec-verify

Conversation

@kpham-sgl

@kpham-sgl kpham-sgl commented Nov 5, 2026 •

Copy link
Copy Markdown
Collaborator

Original authors: @yhyang201

Stacks on #48721. The diff includes #48721 until it merges; the commits of this PR start at "Raw port of DSPARK and DFLASH under DCP".

Port of #48414

On top of the Kimi-K3.1 DCP decode runtime (#48721), this ports the branch's speculative decoding under DCP. DSA target verify attends over each rank's shard and returns (o, lse) partials for the existing DCP merge, as decode does. The indexer scores each verify row's local share of its causal length; on SM100, DG-native paged MQA logits read a request's index-K once for all of its draft rows. The Kimi-K3.1 DCP override admits DSPARK (static ragged verify, decode attention mode) and DFLASH (cutedsl_mla draft backend, decode attention mode). DFLASH MLA drafts are no longer rejected under DCP: they run the target's DCP decode path. The target's pool budget charges the DFLASH draft KV once per draft pool row per target row, not per widened location (#64 on the branch).

Against the raw port (first commit), the draft-backend check reads the draft config through main's get_config / parse_dflash_draft_config(...).is_mla instead of the branch's is_dflash_mla_draft_config. Only cutedsl_mla is admitted as an MLA-only DFLASH draft backend; the branch also admits trtllm_mla, which needs the sliding-window draft table work. For the draft KV budget, the branch's mla_draft_rows_are_dcp_collapsed predicate and its resolver are replaced by main's rule. draft_loc_space_scale(hf_config=, use_mla_backend=, dcp_size=) now holds KVCacheConfigurator.loc_space_scale's draft rule; the target-side configurator resolves dflash_draft_loc_space_scale from the draft model config with it. _dflash_draft_cell_size and the layer-count fallback scale the draft by that instead of attn_dcp_size. API changes for the branch's callers: _dflash_draft_loc_scale / dflash_draft_rows_dcp_collapsed become KVCacheConfigurator.dflash_draft_loc_space_scale.

Not taken, because main's design covers it: the branch's DCP-collapsed draft row layout (draft_mla_rows_are_physical, mla_draft_rows_are_dcp_collapsed). On main, mla_attention_dcp_size already keeps a DCP-executing MLA draft on per-rank rows (loc_space_scale == 1).

Left for later ports:

  • Sliding-window MLA drafts on trtllm_mla / cutedsl_mla under DCP.
  • The context-split verify indexer and the DCP top-k merge split mode; their only caller is the megakernel verify path.
  • EAGLE3 MLA drafts under DCP.
  • PD and HiCache under DCP.

Modifications

  • dsa_backend.py: target verify under DCP skips the prefill KV gather and asks _forward_flashmla_kv for a partial. Verify rows get this rank's local lengths, laid out [B, next_n] for DG-native. The captured-verify DG-native refresh stays off under DCP.
  • dsa_indexer.py: DG-native paged MQA logits for DCP verify on SM100.
  • kimi_k3.py override: DSPARK and DFLASH rules in the DCP branch of _kimi_k31_overrides. PD, HiCache, HiSparse and prefill CP stay rejected.
  • speculative_hook.py, draft_worker_common.py: cutedsl_mla is a DFLASH draft backend for MLA drafts.
  • models/dflash.py: drop the --dcp-size > 1 rejection of MLA drafts.
  • kv_cache_configurator.py, pool_configurator.py: the DFLASH draft KV is charged at cell_size * dflash_draft_loc_space_scale: 1 for an MLA draft that executes DCP, attn_dcp_size for a draft that spans the widened locs. Before, it was always attn_dcp_size.

Tests

Static checks: pre-commit and ruff --select F on the touched files. test_pool_configurator.py covers the draft charge at loc_space_scale 1 and 8 and draft_loc_space_scale itself; it has not been run yet (pending a /rerun-test). GPU validation owed: Kimi-K3.1 TP8 / DCP8 with DSPARK (SGLANG_RAGGED_VERIFY_MODE=static) and with an MLA DFLASH draft on B300, checking accept length and GSM8K (sgl-eval) against DCP1.


CI States

Latest PR Test (Base): ❌ Run #42156554976
Latest PR Test (Extra): ❌ Run #42156554981
Latest PR Test (AMD ROCm 10): ❌ Run #42156555020

Tip

Base CI only covers the core cases, and its workload may not exercise your change. Use /rerun-test to run the tests your change touches, or copy this prompt for an agent:

Find the registered tests this PR affects and run them with /rerun-test, following https://github.com/sgl-project/sglang/blob/main/.agents/skills/rerun-test/SKILL.md

yhyang201 and others added 6 commits November 5, 2026 12:46
…ject#48414

Copy the DCP decode-runtime hunks of the Kimi-K3.1 Day-0 branch onto
main as they are: index-K sharded by token, the local top-k plus global
merge in the indexer, the DSA backend's DCP prefill gather and decode
partials, and the Kimi-K3.1 DCP override. Follow-up commits adapt it to
main's APIs and narrow its scope.

Co-authored-by: Khoa Pham <khoa.pham@radixark.ai>
Co-authored-by: Xiaoyu Zhang <1182563586@qq.com>
Co-authored-by: Po-Han Huang <53919306+nvpohanh@users.noreply.github.com>
Co-authored-by: Yuwei An <ayw.sirius19@gmail.com>
- dsa_dcp_sharded: build the merge with DcpTopKMerge.create (sgl-project#48588),
  once per process even when it declines; call the candidate, paged and
  packed top-k with dcp_size / dcp_rank / deterministic and the default
  cluster choice (sgl-project#48501).
- deepseek_v4_backend: pass the FlashMLA schedule as schedule= (sgl-project#48501).
- dsa_backend: run FlashMLA through flash_mla_interface and the
  backend's _call_* hooks instead of sgl_kernel.flash_mla.
- Drop the native-head FlashMLA branches; their kernel is not on main.
- The override rejects speculative decoding, PD disaggregation, HiCache,
  HiSparse and prefill CP under DCP; their DCP paths stay on sgl-project#48414.
- The DSA backend requires flashmla_kv for prefill and decode under DCP;
  trtllm and Prims-TS DCP are not ported.
- The candidate all-gather uses the multimem all-gather or the group
  all-gather; the prefix-only gather kernel is not on main.
…oject#48414

Copy the speculative hunks of the Kimi-K3.1 Day-0 branch's DCP runtime
onto the DCP decode port as they are: DSA target verify returns per-rank
partials, the verify rows' local lengths (DG-native on SM100), the
DSPARK and DFLASH branches of the Kimi-K3.1 DCP override, and DFLASH MLA
drafts on the MLA-only draft backends under DCP. Follow-up commits adapt
it to main.
…sl_mla

- The draft-backend check loads the draft config with get_config and
  reads parse_dflash_draft_config(...).is_mla, as main's trtllm_mha
  check does, instead of the source's is_dflash_mla_draft_config.
- Only cutedsl_mla is an MLA-only DFLASH draft backend here; trtllm_mla
  needs the sliding-window draft table work that is not ported.
- The DFlashMLAAttention comment points at mla_attention_dcp_size, which
  keeps a DCP MLA draft on per-rank owner rows on main.


Copy the Kimi-K3.1 Day-0 branch's DFLASH draft KV budget under DCP as it
is: the target's pool budget charges a draft whose MLA rows stay per
rank once per target row, not per widened location. A follow-up commit
adapts it to main's loc_space_scale rule.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

The target's pool budget charged the DFLASH draft KV at attn_dcp_size
times its cell size, as if the draft pool always spanned the widened
locs. On main an MLA draft that executes DCP (mla_attention_dcp_size)
keeps per-rank rows, so its loc_space_scale is 1 and the charge was
dcp_size times too high.

- draft_loc_space_scale(hf_config=, use_mla_backend=, dcp_size=) holds
  KVCacheConfigurator.loc_space_scale's draft rule; the draft property
  calls it, and the target-side configurator resolves
  dflash_draft_loc_space_scale from the draft model config with it.
- _dflash_draft_cell_size and the layer-count fallback scale the draft by
  dflash_draft_loc_space_scale instead of attn_dcp_size.
- Drop the source's mla_draft_rows_are_dcp_collapsed predicate and its
  resolver; main's rule replaces them.
- The unit test covers both scales and draft_loc_space_scale itself.
@kpham-sgl

Copy link
Copy Markdown
Collaborator Author

/rerun-test test/registered/unit/model_executor/test_pool_configurator.py

@github-actions

github-actions Bot commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

⚠️ Rebase Required Before Re-run

A major update has landed on main. Your PR is diverged relative to required base commit 50322ac7ba85.

Re-run was not dispatched. What to do:

  • Rebase your branch onto the latest main and push again
  • Follow issue #21065 for context
  • CI-fix PRs may request the bypass-maintenance label to skip this check

- Target verify is rejected under DCP for now, so the indexer and the
  backend take only decode rows: remove the verify branches, the verify
  wording, and the draft-token factor in the merge row count.
- Build the candidate all-gather only when the fused merge declines;
  init_dcp_topk_merge returns whether it is in use. Drop the unused
  all_gather parameter of dcp_global_topk.
- Assert that the FlashMLA head count matches the one its schedule
  metadata was built for.
- Condense the multi-line comments the port added, and note that the
  FlashMLA schedule constants also cover the head64x2 (h128/d576) path.
Comment and docstring changes only (AST-identical apart from docstrings).
Adds a TODO to budget the dense merge scratch, and restores main's
index-K sizing comment with its wording updated for K3.1.
Handle --dcp-size > 1 inside _kimi_k31_overrides, like Kimi-K3's DCP
branch: reject speculative decoding, PD and HiCache, and default the
replicated q projection. Drop the power-of-two rule and the checks the
shared hooks already make (DCP divides TP, no prefill CP, no HiSparse
for K3.1), and restore Kimi-K3's own block to main.
Use the same predicate the DSA backend checks instead of getattr on
index_topk.
Drop the scattered K3.1 one-line comments, restore main's wording on the
comments the port had edited, and keep one NOTE on the flag in
layers/dcp/layout.py.
save_kv_cache is always True here, the RadixAttention default; the source
needs it only for its side-stream KV write, which is not ported.
…ackend

Follow the MLA DCP split of ownership with DSA-specific entry points:
- prepare_decode_context_parallel_metadata_dsa (planner) builds this
  rank's prefix rows and a buffer laid out request after request, the
  order the ragged top-k indices address;
- all_gather_kv_cache_for_extend_dsa (comm) all-gathers the cached
  prefix rows and quantizes the extend tokens as the pool writes them;
- bcg_dcp_extend_kv_gather_dsa (forward_mla) and the MHA one-shot path
  call it; the DSA backend only reads attn_dcp_metadata.dcp_kv_buffer.
Kimi-K3 picks the DSA planner when index-K is sharded, so K3.1 no longer
allocates the unused MLA gather buffer.
…he DSA backend"

This reverts commit 2d3e598; the change goes to the source branch first
so the port stays a port.
Pass an empty FlashMLASchedMeta so FlashMLA builds the split-KV schedule
from topk_length, instead of precomputing a windowed one through the
DeepSeek-V4 backend's private helper. deepseek_v4_backend.py is back to
main; the precomputed schedule is a decode speedup for a later port.
Restore main's padding ladder for the decode head count, add the same for
the DCP prefill (local) head count, and pick between them in
_forward_flashmla_kv instead of recomputing from the tensor and asserting.
Drop _validate_dcp_backend from the DSA backend. Its backend check is
already enforced by the Kimi-K3.1 DSA backend pin and its model check
cannot fail; the K3.1 DCP override now rejects a non-fused or non-v2
sgl-kernel top-k before the model loads.
Replace the backend's dcp_size/dcp_rank (replicated index-K DCP) and the
port's attn_dcp_size/attn_dcp_rank (sharded index-K DCP) with one
attn_dcp_size/attn_dcp_rank, the DCP width this backend runs at, and a
dsa_shards_index_k flag for the mode. Sites of one mode test the flag;
non-K3.1 models evaluate every site as before.
…o kpham/k31-dcp-spec-verify

# Conflicts:
#	python/sglang/srt/arg_groups/model_overrides/kimi_k3.py
sgl-project#48721 dropped its target-verify branches and moved the sharded-mode
checks to dsa_shards_index_k. Restore verify on top of that shape:
- the paged top-k and the k-only path take target-verify rows;
- prefill gathers KV only outside target verify;
- the merge sizes rows by draft tokens;
- the verify-only sites test dsa_shards_index_k instead of the DCP width.
The Kimi-K3.1 DCP override admits DSPARK (static ragged verify) and
DFLASH (cutedsl_mla draft) inline, with decode attention mode for verify.
Three tests build DeepseekSparseAttnBackend with __new__; set the
attn_dcp_size/attn_dcp_rank, dsa_shards_index_k, real_page_span and
prefill head fields that __init__ now sets for a non-DCP backend.
The remaining tests and the dsa_metadata_kit helper that build
DeepseekSparseAttnBackend without __init__ now set dsa_shards_index_k,
attn_dcp_size/attn_dcp_rank and, where they set a page size,
real_page_span, as __init__ does for a non-DCP backend.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants