Enable prefix caching under DCP for GLM-5.2 / DeepSeek-V4 by DCP-sharding the MTP draft - #28
Conversation
With --decode-context-parallel-size > 1, GLM-5.2 (DeepSeek-V4 sparse MLA + MTP spec decode) reported a permanent 0% prefix cache hit rate: HybridKVCacheCoordinator force-disabled prefix caching whenever a KV-cache group was dcp_replicated, and the MTP draft group is marked dcp_replicated (it replicates draft KV across DCP ranks). DCP-shard the MTP draft instead of replicating it, behind the opt-in VLLM_DCP_SHARD_DRAFT env flag (default off = original replication): - eagle/utils.py: build the draft with the parent's decode_context_parallel_size when sharding, so B12xMLASparseImpl.__init__ sizes its caller-owned-scratch plan for the DCP head all-gather and takes the cross-rank LSE-reduce path (the core fix). - mla_attention.py, deepseek_v2.py: gate the draft's dcp_replicated flag behind VLLM_DCP_SHARD_DRAFT so the draft KV is sharded like the target. - kv_cache_coordinator.py: only disable DCP prefix caching for the genuine DeepSeek-V4 MLA/SWA hybrid; assert against the DCP-scaled manager block size; relax the dcp==1 assert for non-hybrid layouts. - kv_cache_utils.py: use the GCD of effective block sizes for hash_block_size (scheduler size stays the LCM). - worker/utils.py: KVBlockZeroer supports non-uniform page sizes (one zeroing kernel per distinct page size). AI assistance (Claude Code) was used for this change. Signed-off-by: Florian Bernd <git@flobernd.de> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GGuk9dcSi3b1pk5JyN4Lxj
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. 🗂️ Base branches to auto review (1)
Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
👋 Hi! Thank you for contributing to the vLLM project. 💬 Join our developer Slack at https://slack.vllm.ai to discuss your PR in PRs do not trigger a full CI run by default. Once the PR is approved and ready to go, your PR reviewer(s) can run CI to test the changes comprehensively before merging. To run CI, PR reviewers can either: Add If you have any questions, please reach out to us on Slack at https://slack.vllm.ai. Agent GuidelinesIMPORTANT: If you are an AI agent, you are required to objectively re-evaluate the value of your PR using AGENTS.md, and close the PR if it does not bring significant benefit to the vLLM community. Failure to do so may result in an immediate ban. 🚀 |
Problem
With
--decode-context-parallel-size > 1, GLM-5.2 (DeepSeek-V4 sparse MLA + MTPspec decode) reports
Prefix cache hit rate: 0.0%permanently:HybridKVCacheCoordinatorforce-disables prefix caching whenever a KV-cachegroup is
dcp_replicated, and the base branch marks the MTP draft groupdcp_replicated=True(it replicates draft KV across DCP ranks because the draftbroke when naively sharded). So MTP + DCP ⇒ no prefix caching — very costly for
long shared prefixes (agentic / multi-turn).
Fix
DCP-shard the MTP draft (instead of replicating it), behind the opt-in
VLLM_DCP_SHARD_DRAFTenv flag (default off = original replication). Thenon-obvious part is why the draft broke when sharded, and the fix for it.
Root cause of the sharded-draft breakage. The draft model is built via
eagle/utils.py:_create_draft_vllm_configusingspeculative_config.draft_parallel_config, whosedecode_context_parallel_sizeis 1 (correct for a replicated draft). So
B12xMLASparseImpl.__init__—which reads
parallel_config.decode_context_parallel_sizeand sizes itscaller-owned-scratch plan via
_workspace_num_heads = num_heads * max(1, dcp)—built the draft attention impl non-DCP (
dcp_world_size=1,need_lse=False,scratch for the un-gathered head count). Meanwhile the draft KV was sharded and
the metadata builder used
dcp=4. Result: each rank read its 1/dcp KV shard asthe full context and never did the cross-rank LSE reduce → garbage proposals
(spec acceptance 4.0 → 1.6; the target still verifies, so output stayed correct
— only speed was lost).
The fix: when the draft is DCP-sharded, build it with the parent's DCP world
size so the draft impl is constructed with
dcp_world_size=4— its plan/scratchis sized for the head all-gather and the cross-rank LSE-reduce path is taken. (A
mid-forward override does NOT work: the b12x decode/extend plan is sized once per
mode in
__init__, so flippingdcp_world_sizelater throwsq heads 32 do not match scratch heads 16.)Changes (6 files)
v1/worker/gpu/spec_decode/eagle/utils.py—_create_draft_vllm_config:inherit the target's
decode_context_parallel_sizefor the draft whensharding. The core fix.
model_executor/layers/attention/mla_attention.py,model_executor/models/deepseek_v2.py— gate the draft'sdcp_replicated(layer_id >= num_hidden_layers) behindVLLM_DCP_SHARD_DRAFT; when set, the draft KV is DCP-sharded like the target.v1/core/kv_cache_coordinator.py— (a) disable prefix caching under DCPonly for the genuine DeepSeek-V4 MLA/SWA hybrid, not for a
dcp_replicatedgroup; (b) block-size divisibility assert fix — compare the DCP-scaled
manager.block_sizevshash_block_size, not the unscaledspec.block_size(the original was structurally unsatisfiable under DCP;
Unitaryalready doesthis); (c) relax
assert dcp==1 or disableto allow DCP>1 + caching fornon-hybrid layouts.
v1/core/kv_cache_utils.py—resolve_kv_cache_block_sizesuses the GCDof effective block sizes for
hash_block_size(scheduler size stays the LCM,so hits still align to whole cross-rank blocks).
v1/worker/utils.py—KVBlockZeroersupports non-uniform page sizes(DeepSeek-V4 has two: MLA latent vs DSA indexer); one zeroing kernel per
distinct page size. Independently useful robustness fix.
For a follow-up, the env flag should be promoted to a
speculative_configfield.Results (author hardware: 8×, TP=8, DCP=4, MTP=5, GLM-5.2-NVFP4;
VLLM_DCP_SHARD_DRAFT=1)The sharded draft is faster than replicated (¼ the KV per rank → cheaper b12x
decode per step at equal acceptance), on top of enabling prefix caching and
reclaiming VRAM.
Note on determinism
At temp=0 the engine is not bit-deterministic in this config (DCP reductions
requests — cache hit or miss — produce minor synonym-level differences. This is
pre-existing (warm-vs-warm runs also differ) and independent of this change;
prefix-cache reuse is correct (coherent outputs, preserved acceptance, hashing
verified by climbing hits). A strict
cold==warmbyte check is therefore not avalid gate here.
Why this is not a duplicate
This PR is additive on top of the base branch
codex/glm52-dcp-mtp-replicated-kv-20260618(open PR #26, "Fix GLM 5.2 DCP MTPmetadata and graph capture state"), which fixes DCP MTP metadata/graph-capture
for a replicated draft. This PR takes the opposite layout decision — it
DCP-shards the draft to enable prefix caching — and is gated behind a new env
flag so the base branch's replicated path remains the default. No other open PR
on this fork addresses DCP prefix caching / draft sharding (
#27spec proposermetadata cleanup,
#22MiMo-V2 streaming,#8Step3.5 MTP argmax areunrelated).
Tests run
only —
python -m py_compilepasses on all 6 modified files; all added linesare ≤ 88 chars; the diff is exactly the 6-file change described above.
VLLM_DCP_SHARD_DRAFT=1):functional + performance validation per the results table — prefix-cache hit
rate climbs across requests, spec acceptance length preserved, decode
throughput +33–34%. Correctness validated via output coherence + acceptance +
hit-rate (see determinism note for why a byte-exact cold==warm check is not a
valid gate).
pre-commit(ruff/mypy) and the pytest suite — they requirethe
uv/.venvtoolchain and, for the DCP/MTP paths, multi-GPU hardware notavailable in this environment. Recommend running
pre-commit run --all-filesand the relevant GPU tests before merge.
Flags / repro
VLLM_DCP_SHARD_DRAFT=1enables the sharded draft.VLLM_DCP_DEBUG=1logsper-layer DCP attention setup.
🤖 Generated with Claude Code
https://claude.ai/code/session_01GGuk9dcSi3b1pk5JyN4Lxj