Conversation
Count speculative verification rows in DCP top-k workspace estimates and clarify the reserved buffer scope. Extend cached-prefill numerical coverage to DCP2/DCP4 and BF16/FP8 caches. Validation: 53 executor/DCP CPU tests and 14 DCP attention tests passed on one H200 GPU. Ruff and git diff --check passed.
Preserve DCP cache ownership and metadata alongside upstream piecewise CUDA graph entry points. Retain both DCP and TileLang numerical coverage, reject the unsupported TileLang attention plus DCP combination, and remove the requested MTP loader test. Validation: 233 CPU tests passed (16 skipped); all 40 MLA/indexer tests passed on one H200 GPU.
Fuse rank-local index filtering and ordered compaction, avoid packing contiguous queries before all-gather, and clarify DCP helper names. Remove full-score NaN sanitation while preserving index masks and empty-shard attention handling. Widen candidate score addressing to int64 for large CUDA graph buffers and cover the affected paths with regression tests.
There was a problem hiding this comment.
🟡 Changes recommended
Five unresolved review findings, including four critical issues, must be addressed before approval.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Adds opt-in decode context parallelism for PyTorch MLA, including sharded KV handling, distributed attention merging, sparse top-k selection, scheduling, and CUDA-graph support.
Changes:
- Adds DCP configuration, process groups, logical-block scheduling, KV writes, and workspace management.
- Implements dense/sparse FlashMLA decode and prefill paths, MTP metadata, and CUDA-graph support.
- Adds CLI wiring, documentation, and focused correctness tests.
File summaries
| File | Summary |
|---|---|
tests/pytorch/test_dcp.py |
Core DCP planning, allocation, causal-length, and metadata tests. |
tests/pytorch/nn/test_nsa.py |
NSA scoring and metadata coverage. |
tests/pytorch/kernel/test_sparse_index_topk.py |
Distributed sparse top-k tests. |
tests/pytorch/kernel/test_mla_attention.py |
MLA gathering, merging, and prefix-cache tests. |
tests/pytorch/kernel/test_flash_attention.py |
Partitioned attention and LSE merge tests. |
tests/pytorch/kernel/test_fill_kv_cache.py |
DCP KV-cache write tests. |
tests/pytorch/kernel/test_dsa_indexer_preprocess.py |
DCP DSA preprocessing tests. |
tests/pytorch/engine/test_executor_base.py |
DCP workspace setup tests. |
lmdeploy/pytorch/paging/scheduler.py |
DCP logical block sizing and scheduling. |
lmdeploy/pytorch/paging/block_manager/default_block_manager.py |
DCP-aware allocation; critical finding (1 vote) covers unsupported window-attention accounting. |
lmdeploy/pytorch/paging/block_manager/__init__.py |
Passes DCP settings to block managers. |
lmdeploy/pytorch/models/utils/cudagraph.py |
DCP CUDA-graph buffers and metadata. |
lmdeploy/pytorch/models/deepseek_v32_mtp.py |
MTP model state handling. |
lmdeploy/pytorch/kernels/cuda/step_metadata/fill_graph_common_inputs.py |
DCP graph input lengths. |
lmdeploy/pytorch/kernels/cuda/step_metadata/fill_dsa_indexer_metadata.py |
Per-query DCP DSA metadata. |
lmdeploy/pytorch/kernels/cuda/sparse_index_topk.py |
Sparse top-k constant updates. |
lmdeploy/pytorch/kernels/cuda/sparse_index_dcp_topk.py |
Distributed candidate packing and merging. |
lmdeploy/pytorch/kernels/cuda/flashattention.py |
Optional attention LSE output. |
lmdeploy/pytorch/kernels/cuda/fill_kv_cache.py |
Interleaved DCP KV-cache writes. |
lmdeploy/pytorch/kernels/cuda/dsa_indexer_preprocess.py |
DCP-aware DSA cache preprocessing. |
lmdeploy/pytorch/kernels/cuda/dcp.py |
DCP compaction, merging, and reordering kernels. |
lmdeploy/pytorch/engine/executor/base.py |
DCP workspace reservation; moderate finding (1 vote) covers under-budgeted merge buffers. |
lmdeploy/pytorch/engine/engine.py |
DCP session-length and cache-capacity accounting. |
lmdeploy/pytorch/engine/config_builder.py |
Propagates DCP configuration. |
lmdeploy/pytorch/distributed.py |
DCP process groups and collectives. |
lmdeploy/pytorch/config.py |
DCP configuration validation. |
lmdeploy/pytorch/backends/nsa.py |
DCP NSA metadata. |
lmdeploy/pytorch/backends/cuda/nsa.py |
DCP DSA scoring and top-k handling. |
lmdeploy/pytorch/backends/cuda/attention/sparse_mla.py |
Sparse MLA DCP paths; critical finding (2 votes) covers the TileLang override bypass. |
lmdeploy/pytorch/backends/cuda/attention/mla.py |
Dense MLA DCP execution. |
lmdeploy/pytorch/backends/cuda/attention/default.py |
DCP KV integration; critical finding (3 votes) covers non-FlashMLA decode paths. |
lmdeploy/pytorch/backends/cp_utils.py |
DCP planning, gathering, and merge helpers. |
lmdeploy/messages.py |
Public DCP engine configuration; critical finding (1 vote) covers non-CUDA validation. |
lmdeploy/cli/utils.py |
Defines the DCP CLI argument. |
lmdeploy/cli/serve.py |
Server CLI configuration wiring. |
lmdeploy/cli/cli.py |
Chat CLI configuration wiring. |
docs/en/advance/context_parallel.md |
Documents DCP usage and limitations. |
Review details
Suppressed comments (2)
lmdeploy/messages.py:610
- DCP changes the scheduler/trie unit to
block_size * dcp, so one allocated connector block represents a DCP-wide logical block. Mooncake still rounds and hashes by the physicalblock_size; after the first DCP-wide block, its hash count andrequest_blockslength diverge (and loads/saves raise the connector block-count errors). Since external connectors are documented as unsupported, rejectdcp > 1whenkv_transfer_configis configured instead of accepting this combination.
assert self.dcp >= 1, 'invalid dcp'
assert self.tp % self.dcp == 0, 'tp must be divisible by dcp'
lmdeploy/pytorch/engine/executor/base.py:245
- The temporary merge buffers operate on the gathered head count (
local_heads * dcp), but this reservation only budgetslocal_heads. With DCP>1 the cache-size calculation underestimates correction/LSE memory by the DCP factor, so it can allocate a KV cache that later OOMs during attention.
attention_workspace += num_prefill_tokens * local_heads * (model.head_dim * 12 + 12)
- Files reviewed: 37/37 changed files
- Comments generated: 4
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Keep LSE communication and softmax correction arithmetic in FP32, but store corrected contributions in the input dtype for reduce-scatter. Cover both BF16 and FP32 outputs in the correction regression test. Validation: 24 DCP attention tests passed in an isolated dtype-only snapshot.
Cache composite keys across 11-bit radix passes, use block-wide prefix scans, and coalesce candidate loads across warp lanes. Ballot-based emission preserves exact candidate order and bit-preserved INT32 IDs. Extend coverage to DCP8 and large-ID radix boundary ties. Validation: 46 MLA and selector tests passed in an isolated snapshot without communication optimizations; exact-reference, CUDA graph, and sanitizer checks also passed.
| if dcp_world_size == 1: | ||
| return local_output | ||
| from lmdeploy.pytorch.distributed import all_gather_into_tensor, reduce_scatter_tensor | ||
| from lmdeploy.pytorch.kernels.cuda.dcp import correct_dcp_attention_output, sanitize_dcp_lse |
There was a problem hiding this comment.
cuda kernel should not be called here.
| k_seqlens: Tensor | ||
| cu_seqlen_k: Tensor | ||
| k_seqlens: Tensor # global lengths used by interleaved cache writes | ||
| dcp_local_kv_seqlens: Tensor |
There was a problem hiding this comment.
The build of this class would failed on dlinfer backend.
| """Merge in FP32, retaining the prefix output dtype and FP32 LSE.""" | ||
| assert prefix_output.shape == suffix_output.shape | ||
| assert prefix_lse.shape == suffix_lse.shape == prefix_output.shape[:2] | ||
| output = torch.empty_like(prefix_output) |
There was a problem hiding this comment.
Is prefix_output bf16? accumulation on bf16 might loss precision.
There was a problem hiding this comment.
Yes, prefix_output is BF16. Merge arithmetic is FP32, but each result is stored back in BF16, matching vLLM’s MLA chunk merging. This reduces intermediate memory usage and traffic, but repeated rounding can accumulate. We can retain FP32 intermediates if preferred.
| local_count = T.alloc_var(T.int32) | ||
| local_count = 0 | ||
| for i in T.Unroll(keys_per_thread): | ||
| candidate = (tidx // 32) * (32 * keys_per_thread) + i * 32 + tidx % 32 |
There was a problem hiding this comment.
The kernel requires that dcp*k should be multiple of 512, better add an assert in the kernel launcher.
Summary
Add opt-in
--dcpfor PyTorch MLA: shard KV tokens across existing attention-TP ranks to increase cache capacity without increasing model world size.Scope: CUDA FlashMLA, BF16 activations, DP1/EP1, sparse top-k 512/2048. FP8 model weights are supported.
dcp=1is the default; TurboMind--cpis unchanged.Accuracy
Full GLM-5.2-FP8, TP8+DCP4, MTP5, FP8 KV, CUDA graphs and prefix caching enabled; client concurrency 256:
Performance
Full GLM-5.2-FP8 on 8×H200, FP8 KV, MTP5, prefix caching and parsers enabled. SWE-Smith: 16 conversations × 5 turns, concurrency 16, first-turn target 16,384 tokens, subsequent increments 2,048, output 1,024 tokens/request. Each run completed 80/80 requests without errors.
LMDeploy throughput:
15865bcf, existing symmetric-memory all-reduce enabled, experimental DCP communicator excluded. vLLM:0.28.1rc1.dev649+g93911fcf6.Throughput percentages compare matching TP/DCP configurations. Capacity means total resident KV token slots, not per-request context length; do not multiply by TP. LMDeploy excludes its reserved block.
¹ vLLM TP8 used a 65,536-token context limit after 1,048,576 failed startup KV-capacity validation; other runs used 1,048,576.
Measurement notes and validation limits
ag_rsbecause defaulta2afailed with this build's sparse FlashMLA backend.Server and client commands
Run each configuration separately with a fresh server.
MODELpoints to the same full GLM-5.2-FP8 checkpoint.LMDeploy at
15865bcf: run once withDCP=1, once withDCP=4. The distributed executor is auto-selected.vLLM: run with
DCP=4, MAX_MODEL_LEN=1048576, thenDCP=1, MAX_MODEL_LEN=65536. Block size, KV interleave, attention backend, prefix caching and chunked prefill are not explicitly overridden; this build selected block size 64, interleave 1, sparse FlashMLA, and enabled both caching and chunked prefill.EvalScope 1.9.1 client arguments, identical for all four runs:
ModelScope was unavailable, so the actual runs used a wrapper reading the same cached raw dataset with unchanged live construction. All 16 first-turn request hashes and dataset shard manifests matched across runs.
Assistance
Assisted with Codex + GPT-6-Astra High, reviewed manually