feat(dcp): add native depth-N CKV prefetch#160
Conversation
|
Warning Review limit reached
Next review available in: 9 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThis PR adds DCP-replicated sparse-indexer support, lockstep MLA KV-cache allocation, and workspace-scoped B12X CKV prefetching. It updates indexer metadata and cache coordination paths, adds environment controls, and expands coverage across replication, allocation, workspace lifecycle, and attention behavior. ChangesDCP-replicated indexer and lockstep MLA allocation
Estimated code review effort: 5 (Critical) | ~120 minutes Sequence Diagram(s)sequenceDiagram
participant Config
participant IndexerCache
participant IndexerMetadata
participant SparseIndexer
Config->>IndexerCache: evaluate replication policy
IndexerCache->>IndexerCache: create replicated cache spec
IndexerCache->>SparseIndexer: pass dcp_replicated
IndexerMetadata->>IndexerMetadata: use global sequence lengths
SparseIndexer-->>IndexerMetadata: retain global top-k IDs
sequenceDiagram
participant MetadataBuilder
participant CKVRegistry
participant B12xForward
participant CUDAStream
MetadataBuilder->>CKVRegistry: begin workspace step
B12xForward->>CKVRegistry: borrow ring workspace
B12xForward->>CUDAStream: enqueue lookahead gather
CUDAStream-->>CKVRegistry: record completion event
B12xForward->>CKVRegistry: wait and append gathered history
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ 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. 🚀 |
Signed-off-by: FujitsuPolycom <87842395+FujitsuPolycom@users.noreply.github.com>
|
Validated on ai01 (4x RTX PRO 6000, TP4/DCP4/MTP3) against the RAM-only LMCache integration. The original failure occurred when a 189,033-token request resumed from a 187,136-token LMCache prefix and B12X returned a valid token-major projection buffer. The previous guard accepted only head-major storage and terminated all workers. Post-fix reproducer:
Exact-image unit gate: 30 passed. The projection still requires the expected shape/dtypes and borrowed-storage alias; only the two dense layouts consumed correctly by the existing transpose/copy are accepted. |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@vllm/model_executor/models/deepseek_v2.py`:
- Around line 691-703: Update Indexer.__init__() to pass the cache
specification’s effective replicated flag, including the draft_replicated result
from DeepseekV32IndexerCache.get_kv_cache_spec(), into SparseAttnIndexer. Reuse
the MLAAttentionSpec.dcp_replicated value rather than only
self.k_cache.dcp_replicated, keeping metadata and custom-op dcp world-size
initialization consistent.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 8d709f7b-7e76-4fd6-9cac-508f02f5bc81
📒 Files selected for processing (17)
tests/model_executor/layers/test_sparse_attn_indexer_b12x.pytests/models/test_dcp_shard_draft_defaults.pytests/v1/attention/test_b12x_ckv_prefetch_policy.pytests/v1/attention/test_b12x_mla_dcp_workspace.pytests/v1/attention/test_indexer_dcp_localize.pytests/v1/core/test_kv_cache_utils.pytests/v1/core/test_prefix_caching.pytests/v1/worker/test_attn_utils.pytests/v1/worker/test_gpu_block_table.pyvllm/envs.pyvllm/model_executor/layers/sparse_attn_indexer.pyvllm/model_executor/models/deepseek_v2.pyvllm/v1/attention/backends/mla/b12x_mla_sparse.pyvllm/v1/attention/backends/mla/indexer.pyvllm/v1/core/block_pool.pyvllm/v1/core/kv_cache_coordinator.pyvllm/v1/core/kv_cache_utils.py
| draft_replicated = ( | ||
| not shard_draft | ||
| and layer_id is not None | ||
| and num_hidden_layers is not None | ||
| and int(layer_id) >= int(num_hidden_layers) | ||
| ) | ||
| return MLAAttentionSpec( # Only has one vector instead of K + V | ||
| block_size=self.cache_config.block_size, | ||
| block_size=block_size, | ||
| num_kv_heads=1, | ||
| head_size=self.head_dim, | ||
| dtype=self.dtype, | ||
| dcp_replicated=dcp_replicated, | ||
| dcp_replicated=self.dcp_replicated or draft_replicated, | ||
| ) |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Locate where dcp_local_seq_lens is populated for draft/MTP attention metadata
# and whether it is gated on VLLM_DCP_SHARD_DRAFT for replicated drafts.
rg -n "dcp_local_seq_lens" --type=py -g '!tests/*' | rg -v "\.pyc"Repository: local-inference-lab/vllm
Length of output: 162
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Candidate files =="
git ls-files | rg 'vllm/(model_executor/models/deepseek_v2\.py|attention/.*|sparse_attn.*|indexer|.*DCP.*|.*dcp.*)\.py|tests/.*deepseek.*py|tests/.*indexer.*py|tests/.*dcp.*py' | sed -n '1,200p'
echo
echo "== deepseek_v2.py outline around relevant symbols =="
ast-grep outline vllm/model_executor/models/deepseek_v2.py --match DeepseekV32IndexerCache --view expanded || true
ast-grep outline vllm/model_executor/models/deepseek_v2.py --match get_kv_cache_spec --view expanded || true
ast-grep outline vllm/model_executor/models/deepseek_v2.py --match Indexer --view expanded || true
echo
echo "== deepseek_v2.py relevant lines =="
sed -n '620,835p' vllm/model_executor/models/deepseek_v2.py
echo
echo "== Targeted searches for flag/env names =="
rg -n "VLLM_DCP_(SHARD_DRAFT|REPLICATE_INDEXER_CACHE|DCP_WORLD_SIZE|SHARD)|dcp_replicated|dcp_world_size|draft_replicated|_replicate_indexer_cache_under_dcp|SparseAttnIndexer|DeepseekV32IndexerMetadataBuilder" --type=py -g '!tests/*' | sed -n '1,240p'Repository: local-inference-lab/vllm
Length of output: 11288
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Locate SparseAttnIndexer / DeepseekV32IndexerMetadataBuilder =="
rg -n "class SparseAttnIndexer|class .*IndexerMetadataBuilder|SparseAttnIndexer\(" --type=py --glob '*.py' | sed -n '1,200p'
echo
echo "== Candidate non-test source files =="
git ls-files '*.py' | rg 'vllm/(v1|lora|model_executor|kernels)' | rg -i 'sparse|mha|index|indexer|attention|dcp' | sed -n '1,260p'
echo
echo "== Relevant tests =="
git ls-files 'tests/**/*.py' | rg -i 'sparse_indexer|indexer|deepseek_v2|mtp|dc|dcp' | sed -n '1,260p'Repository: local-inference-lab/vllm
Length of output: 229
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== deepseek_v2.py top imports =="
sed -n '1,180p' vllm/model_executor/models/deepseek_v2.py
echo
echo "== All SparseAttnIndexer/indexer metadata occurrences =="
rg -n "SparseAttnIndexer|DeepseekV32IndexerMetadataBuilder|MLAAttentionSpec|dcp_replicated|dcp_world_size|B12xNonCompressedIndexerBackend|DeepseekV32IndexerBackend" --glob '*.py' | sed -n '1,260p'
echo
echo "== v1 attention backend index files =="
find vllm -path '*attention*' -name '*.py' -print | sed -n '1,200p'Repository: local-inference-lab/vllm
Length of output: 6833
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== sparse_attn_indexer source =="
find . -path '*/vllm/model_executor/layers/sparse_attn_indexer.py' -print -exec wc -l {} \;
sed -n '1,260p' vllm/model_executor/layers/sparse_attn_indexer.py
echo
echo "== sparse_attn_indexer tests =="
find tests -name '*sparse*indexer*' -o -name '*deepseek*index*' | sed -n '1,120p'
for f in $(find tests -name '*sparse*indexer*' -o -name '*deepseek*index*' | head -20); do
echo "--- $f ---"
wc -l "$f"
rg -n "SparseAttnIndexer|dcp_replicated|draft|VLLM_DCP_SHARD_DRAFT" "$f" || true
done
echo
echo "== Related envs =="
sed -n '1,220p' vllm/envs.py | rg -n "DCP|SHARD_DRAFT|REPLICATE|DCP_WORLD_SIZE" -C 2 || trueRepository: local-inference-lab/vllm
Length of output: 10087
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== SparseAttnIndexer class and __init__ =="
sed -n '220,450p' vllm/model_executor/layers/sparse_attn_indexer.py
echo
echo "== DeepseekV32IndexerMetadata/DeepseekV32IndexerMetadataBuilder =="
rg -n "class DeepseekV32IndexerMetadata|class DeepseekV32IndexerMetadataBuilder|dcp_world_size|dcp_replicated|dcp_local_seq_lens" vllm/model_executor/layers/sparse_attn_indexer.py vllm/v1/attention vllm/model_executor/layers/sparse_attn_indexer.py | sed -n '1,240p'
echo
echo "== Relevant backend classes =="
sed -n '1,420p' vllm/v1/attention/backends/mla/indexer.py
echo
echo "== Behavioral verifier for draft_replicated vs k_cache.dcp_replicated =="
python3 - <<'PY'
from dataclasses import dataclass
`@dataclass`
class ParallelConfig:
decode_context_parallel_size: int
prefill_context_parallel_size: int
`@dataclass`
class Config:
envs_shard_draft = None
layer_id = None
num_hidden_layers = None
dcp_replicated = False
use_v2_model_runner = False
dcp_size = 4
pcp_size = 1
def _replicate_indexer_cache_under_dcp(cfg):
if cfg.layer_id is None or cfg.num_hidden_layers is None:
return False
if cfg.dcp_size * cfg.pcp_size <= 1:
return False
if int(cfg.layer_id) >= int(cfg.num_hidden_layers):
return False
requested = cfg.envs_shard_draft
return requested
for env in [None, "1", "true", "0", "false"]:
cfg = Config(envs_shard_draft=env, layer_id=10, num_hidden_layers=8)
cache_flag = _replicate_indexer_cache_under_dcp(cfg)
shard_draft = ("1" if cfg.envs_shard_draft is None else cfg.envs_shard_draft).lower() in ("1", "true", "yes")
spec_flag = cache_flag or (not shard_draft and cfg.layer_id is not None and cfg.num_hidden_layers is not None and int(cfg.layer_id) >= int(cfg.num_hidden_layers))
print({
"VLLM_DCP_SHARD_DRAFT": env,
"k_cache.dcp_replicated": cache_flag,
"MLAAttentionSpec.dcp_replicated": spec_flag,
"Indexer passes to SparseAttnIndexer": cache_flag,
"diverges": cache_flag != spec_flag,
})
for layer_id in [6, 7, 8, 9]:
cfg = Config(envs_shard_draft="0", layer_id=layer_id, num_hidden_layers=8)
cache_flag = _replicate_indexer_cache_under_dcp(cfg)
shard_draft = ("1" if cfg.envs_shard_draft is None else cfg.envs_shard_draft).lower() in ("1", "true", "yes")
spec_flag = cache_flag or (not shard_draft and cfg.layer_id is not None and cfg.num_hidden_layers is not None and int(cfg.layer_id) >= int(cfg.num_hidden_layers))
print({
"layer_id": layer_id,
"k_cache.dcp_replicated": cache_flag,
"MLAAttentionSpec.dcp_replicated": spec_flag,
"diverges": cache_flag != spec_flag,
})
PYRepository: local-inference-lab/vllm
Length of output: 48310
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== SparseAttnIndexer constructor and build metadata =="
sed -n '2144,2270p' vllm/model_executor/layers/sparse_attn_indexer.py
sed -n '2268,2296p' vllm/model_executor/layers/sparse_attn_indexer.py
echo
echo "== SparseAttnIndexer call sites from indexers =="
rg -n "SparseAttnIndexer\(" --glob '*.py' | sed -n '1,120p'
echo
echo "== DeepseekV32IndexerMetadataBuilder build calls and local seq lens =="
sed -n '420,550p' vllm/v1/attention/backends/mla/indexer.py
sed -n '860,1100p' vllm/v1/attention/backends/mla/indexer.py
rg -n "build_indexer_metadata|get_attn_metadata|deepseek_v2_indexer|SparseAttnIndexer" --glob '*.py' | sed -n '1,200p'
echo
echo "== Behavioral verifier for draft_replicated vs k_cache.dcp_replicated =="
python3 - <<'PY'
def replicate_indexer_cache(layer_id, num_hide, shard_draft_env):
if layer_id is None or num_hide is None:
return False
raw = shard_draft_env
shard_draft = ("1" if raw is None else raw).lower() in ("1", "true", "yes")
return not shard_draft
def spec_draft_layer_flag(layer_id, num_hide, shard_draft_env, replicate_flag):
shard_draft = ("1" if shard_draft_env is None else shard_draft_env).lower() in ("1", "true", "yes")
draft_replicated = (
not shard_draft
and layer_id is not None
and num_hide is not None
and int(layer_id) >= int(num_hide)
)
return replicate_flag or draft_replicated
print("draft=True, VLLM_DCP_SHARD_DRAFT unset:", spec_draft_layer_flag(10, 8, None, 0), "k_cache=0")
print("draft=True, VLLM_DCP_SHARD_DRAFT=false:", spec_draft_layer_flag(10, 8, "0", 0), "k_cache=1")
for layer_id in [7, 8, 9]:
k_cache = replicate_indexer_cache(layer_id, 8, "0")
spec = spec_draft_layer_flag(layer_id, 8, "0", k_cache)
print(f"layer_id={layer_id}: k_cache={k_cache}, spec={spec}, diverges={k_cache != spec}")
PYRepository: local-inference-lab/vllm
Length of output: 6438
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== All occurrences of sparse_attn_indexer =="
rg -n "sparse_attn_indexer|sparse_attn_indexer_cuda|sparse attention indexer|sparse_attn_topk" --glob '*.py' --glob '*.cu' --glob '*.cpp' --glob '*.cc' | sed -n '1,300p'
echo
echo "== Test files =="
git ls-files 'tests/**/*.py' | rg -i 'test_sparse_attn_indexer|test_deepseek_v2_index|test_dcp' | sed -n '1,200p'
echo
echo "== Tests involving dcp_replicated or draft behavior =="
rg -n "dcp_replicated|VLLM_DCP_SHARD_DRAFT|draft_replicated|dc|dcp|next_n|speculative" tests --glob '*.py' | rg -i "indexer|sparse|deepseek|mtp|draft" | sed -n '1,320p'Repository: local-inference-lab/vllm
Length of output: 323
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Files containing vllm.sparse_attn_indexer =="
python3 - <<'PY'
import subprocess
result = subprocess.run(['git','grep','-Il','vllm.sparse_attn_indexer','vllm','tests'], capture_output=True, text=True)
print(result.stdout.strip() or '(no matches)')
print('stderr=', result.stderr.strip()[:500], sep='\n')
PY
echo
echo "== Any source mentioning dcp_world_size and dcp_replicated in same file =="
for f in $(git ls-files '*.py' | xargs rg -l 'dcp_world_size|dcp_replicated'); do
if rg -q 'dcp_world_size' "$f" && rg -q 'dcp_replicated' "$f"; then
echo "--- $f ---"
wc -l "$f"
fi
done
echo
echo "== Read relevant backend metadata and common seq lens implementation =="
sed -n '840,900p' vllm/v1/attention/backends/utils.py
sed -n '960,1080p' vllm/v1/attention/backends/mla/indexer.py
echo
echo "== Read relevant backward/topk path in sparse_attn_indexer =="
sed -n '1620,1945p' vllm/model_executor/layers/sparse_attn_indexer.py
sed -n '1940,2030p' vllm/model_executor/layers/sparse_attn_indexer.pyRepository: local-inference-lab/vllm
Length of output: 29259
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Builder helpers for local DCP seq lens =="
rg -n "_use_dcp_local_kv|_dcp_localize_decode_seq_lens|_prepare_decode_tensors|build_prefill_chunk_metadata" vllm/v1/attention/backends/mla/indexer.py -C 4 | sed -n '1,260p'
echo
echo "== Relevant source slices =="
sed -n '1210,1375p' vllm/v1/attention/backends/mla/indexer.py
sed -n '1375,1470p' vllm/v1/attention/backends/mla/indexer.py
echo
echo "== Read metadata builder's build_decode_tensors implementation =="
sed -n '1022,1205p' vllm/v1/attention/backends/mla/indexer.py
echo
echo "== Read build_prefill_chunk_metadata implementation =="
sed -n '900,1015p' vllm/v1/attention/backends/mla/indexer.pyRepository: local-inference-lab/vllm
Length of output: 25489
Pass the same replicated-cache flag to SparseAttnIndexer.
DeepseekV32IndexerCache.get_kv_cache_spec() marks draft layers replicated with self.dcp_replicated or draft_replicated, but Indexer.__init__() only forwards self.k_cache.dcp_replicated. For the legacy replicated-draft case, the metadata builder then uses dcp_world_size=1 while the indexer op is initialized with dcp_world_size=configured_dcp_world_size, so the custom op can miss its local-localized KV bounds/top-k ranges. Pass the cache-spec replicated flag through to SparseAttnIndexer.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@vllm/model_executor/models/deepseek_v2.py` around lines 691 - 703, Update
Indexer.__init__() to pass the cache specification’s effective replicated flag,
including the draft_replicated result from
DeepseekV32IndexerCache.get_kv_cache_spec(), into SparseAttnIndexer. Reuse the
MLAAttentionSpec.dcp_replicated value rather than only
self.k_cache.dcp_replicated, keeping metadata and custom-op dcp world-size
initialization consistent.
|
The prefetch portion has now been rebuilt as a standalone PR directly against current #177 removes the replicated-indexer stack, preallocates the bounded workspace pool before KV-cache memory planning, isolates target/draft and DBO lanes, and includes the measured memory cost plus depth-0/1/2/3 results. It therefore avoids the optimistic KV accounting caused by lazy post-profile ring allocation while preserving the validated depth-1 overlap. I am leaving this PR open for the author/maintainer to disposition; #177 is the clean merge candidate for CKV layer prefetch only. |
Summary
Add feature-gated full-CKV DCP prefill gather with native depth-N cross-layer lookahead.
For eligible pure-prefill steps, each rank gathers native paged CKV history on a dedicated communicator and runs its local query heads over global CKV. A dedicated ring and side stream can queue future layers; the destination layer patches current-chunk records written after prefetch began before attention consumes the workspace.
Scope
VLLM_B12X_MLA_CKV_GATHERVLLM_B12X_MLA_CKV_PREFETCH_DEPTHLookahead supports
fp8_ds_mlaandnvfp4_ds_mla. All new behavior defaults off.Stack note
This PR is stacked on #159, so GitHub temporarily shows the allocator/indexer prerequisite commits too. The new review unit in this PR is
d6545696ff(feat(dcp): add depth-N native CKV prefetch).Validation
On the final TP4/DCP4/MTP3 integration:
Those figures include the complete integration stack and are evidence of compatibility, not an isolated claim for this commit alone.
Compact-record validation:
Design
GLM-5.2 Sparse CKV under Decode Context Parallelism
Credits
The lookahead design originates from Koush's CKV gather and shared-layer prefetch work and builds on Luke Alonso's B12X/Sparkinfer infrastructure.
Implementation, tests, and documentation were developed with OpenAI Codex assistance and manually reviewed and GPU-validated.
Summary by CodeRabbit
New Features
Bug Fixes