Skip to content

[GG] feat(dcp): support partial replicated sparse-indexer topology - #179

Merged
lukealonso merged 7 commits into
local-inference-lab:dev/gilded-gnosisfrom
voipmonitor:perf/gg-dcp-partial-indexer-topology-20260725
Jul 29, 2026
Merged

[GG] feat(dcp): support partial replicated sparse-indexer topology#179
lukealonso merged 7 commits into
local-inference-lab:dev/gilded-gnosisfrom
voipmonitor:perf/gg-dcp-partial-indexer-topology-20260725

Conversation

@voipmonitor

@voipmonitor voipmonitor commented Jul 25, 2026

Copy link
Copy Markdown

Summary

Add an opt-in partial-replication topology for the GLM sparse-indexer cache.
This PR now builds directly on dev/gilded-gnosis after #159 was merged as
4247d676; it no longer contains or duplicates any #159 commits.

Design

  • VLLM_DCP_INDEXER_SHARDS=0 keeps the indexer sharded by the configured DCP.
  • VLLM_DCP_INDEXER_SHARDS=1 fully replicates the indexer cache.
  • An intermediate divisor creates replicated shard groups. Under DCP8, 4
    creates {0,1,2,3} and {4,5,6,7} indexer groups with cross-replica query
    groups {0,4}, {1,5}, {2,6}, and {3,7}.
  • The main MLA CKV cache remains sharded by the configured DCP.
  • Mixed MLA groups retain one logical block-ID lifecycle while using their own
    physical page geometry, block tables, and slot mapping.
  • Unsupported non-divisors, PCP layouts, and non-V2 runners fail explicitly.

The legacy VLLM_DCP_REPLICATE_INDEXER_CACHE=1 switch remains backward
compatible and maps to one indexer shard. All new behavior defaults off.

Measured impact

GLM-5.2 NVFP4 A16, TP8, MTP0, 8x RTX PRO 6000 Blackwell. Baseline and candidate
used the same image, helper, GPU group, and serial benchmark protocol. The
measured image also contained the independent prefill PRs #177 and #178; this
PR does not include their code.

Topology Prefill 64k Prefill 400k Decode ctx0 / 64k / 400k KV tokens
DCP4 sharded 5,772 5,077.8 67.32 / 65.53 / 65.92 2,088,704
DCP4 partial 2x2 5,836 +/- 23 5,110.2 +/- 7.0 73.19 / 71.50 / 70.03 1,984,256
DCP8 sharded 3,279 2,989.6 66.59 / 66.43 / 66.41 4,174,336
DCP8 partial 2x4 5,772 +/- 6 5,030.5 +/- 1.3 68.02 / 66.06 / 66.48 3,964,928

DCP4 partial improves CC1 decode by 6.25-9.10% and prefill by 0.64-1.1% for a
5.0% KV-capacity cost. DCP8 partial improves prefill by 68.3-76.0%, keeps long
decode effectively in parity, and also costs about 5.0% KV capacity.

Validation

  • Rebased directly onto GG 4247d676, with exactly five [GG] feat(dcp): support partial replicated sparse-indexer topology #179-specific commits.
  • git range-diff confirms semantic parity with the original unique commit
    range; the only behavior adjustment keeps feat(kv-cache): support mixed MLA groups and native offload #159's supported external-load
    contract instead of the obsolete rejection test.
  • All 18 changed Python files pass Ruff check and format check.
  • Focused partial-topology suite: 16 passed, 1 GPU-only test skipped.
  • Broader affected suite: 247 passed and 22 skipped; 14 unrelated cases require
    CUDA platform detection unavailable in the CPU-only test container.
  • Existing E2E TP8/DCP4 partial 2x2 and TP8/DCP8 partial 2x4 startup,
    64k/400k prefill, and CC1 decode validation remains applicable because the
    production diff is semantically unchanged.

The lockstep allocator and full-replication foundation are provided by merged
#159. This PR's review unit is the partial topology, dedicated process groups,
generalized cache geometry, query-split composition, and their tests.

Summary by CodeRabbit

  • New Features

    • Added VLLM_DCP_INDEXER_SHARDS to control sparse-indexer KV sharding/replication within DCP groups.
    • Introduced indexer-aware DCP/query-split process group selection and exposed indexer DCP/query-split group accessors.
    • Updated sparse indexer and KV-cache planning to support replicated and partially replicated layouts.
    • Extended lockstep MLA allocation and KVCache coherence behavior.
  • Bug Fixes

    • Improved DCP attention/indexer group rank handling and empty-key/value reference attention behavior.
    • Fixed lockstep MLA copy-on-write mirroring semantics.
  • Tests

    • Expanded distributed, attention, KV-cache, prefix caching, and GPU slot-mapping coverage for DCP/PCP and partial replication.

@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds indexer-specific DCP/query-split groups, configurable DeepSeek indexer KV-cache sharding, CP-aware cache grouping and sizing, lockstep MLA COW handling, group-aware slot mapping, and expanded distributed and attention tests.

Changes

DCP indexer cache and lockstep allocation

Layer / File(s) Summary
Indexer DCP groups and sparse indexer wiring
vllm/distributed/parallel_state.py, vllm/envs.py, vllm/model_executor/layers/sparse_attn_indexer.py, tests/distributed/*, tests/model_executor/layers/*
Adds configurable indexer shard groups and routes sparse-indexer warmup, query-split, and top-k collectives through indexer-aware groups.
DCP cache layout and metadata contracts
vllm/v1/kv_cache_interface.py, vllm/model_executor/models/deepseek_v2.py, vllm/v1/attention/backends/mla/indexer.py, tests/models/*, tests/v1/attention/*
Adds KV shard-count metadata and validation, wires DeepSeek cache replication into indexer construction, and derives metadata sizing and ranks from effective shard layouts.
Lockstep MLA cache allocation and prefix coordination
vllm/v1/core/kv_cache_utils.py, vllm/v1/core/single_type_kv_cache_manager.py, vllm/v1/core/kv_cache_coordinator.py, tests/v1/core/*
Uses CP shard layouts for cache sizing and grouping, synchronizes partial-prefix COW allocation, and expands lockstep prefix-cache coverage.
Runtime shard sizing and slot mapping
vllm/v1/worker/gpu/*, tests/v1/worker/test_gpu_block_table.py
Computes per-group CP sizes and applies group-local CP ranks when generating slot mappings.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ModelRunner
  participant DeepseekV32IndexerCache
  participant SparseAttnIndexer
  participant IndexerDCPGroup
  ModelRunner->>DeepseekV32IndexerCache: derive KV shard layout
  DeepseekV32IndexerCache->>SparseAttnIndexer: pass replication and shard metadata
  SparseAttnIndexer->>IndexerDCPGroup: select indexer DCP group
  SparseAttnIndexer->>IndexerDCPGroup: gather sparse top-k candidates
Loading

Possibly related PRs

Suggested reviewers: zjy0516

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 39.02% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the core change: adding partial replicated sparse-indexer topology support.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

🧹 Nitpick comments (6)
vllm/distributed/parallel_state.py (1)

1451-1485: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add required Google-style docstrings to the new callables.

  • vllm/distributed/parallel_state.py#L1451-L1485: document accessor returns and _build_indexer_replica_group_ranks arguments, return groups, and ValueError.
  • vllm/model_executor/layers/sparse_attn_indexer.py#L2146-L2163: document dcp_replicated and dcp_kv_shard_count in the constructor API.
  • tests/distributed/test_indexer_parallel_groups.py#L9-L43: add test docstrings.
  • tests/model_executor/layers/test_sparse_attn_indexer_b12x.py#L39-L74: add a docstring describing replicated-constructor behavior.
  • tests/model_executor/layers/test_sparse_attn_indexer_b12x.py#L452-L583: add a docstring describing replicated decode behavior.

As per coding guidelines, Python code must use Google-style docstrings with Args:, Returns:, and Raises: sections.

🤖 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/distributed/parallel_state.py` around lines 1451 - 1485, The new
callables and tests lack the required Google-style documentation. In
vllm/distributed/parallel_state.py:1451-1485, document get_indexer_dcp_group and
get_indexer_query_split_group return values, and document
_build_indexer_replica_group_ranks arguments, returned group lists, and its
ValueError condition using Args:, Returns:, and Raises:. In
vllm/model_executor/layers/sparse_attn_indexer.py:2146-2163, document the
constructor parameters dcp_replicated and dcp_kv_shard_count. Add descriptive
docstrings to the tests in
tests/distributed/test_indexer_parallel_groups.py:9-43,
tests/model_executor/layers/test_sparse_attn_indexer_b12x.py:39-74 for
replicated-constructor behavior, and
tests/model_executor/layers/test_sparse_attn_indexer_b12x.py:452-583 for
replicated decode behavior.

Source: Coding guidelines

tests/models/test_dcp_shard_draft_defaults.py (1)

47-58: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

_get_indexer_spec duplicates the cache's own shard/replication derivation.

_get_indexer_spec manually recomputes dcp_replicated/dcp_kv_shard_count (lines 54-57) using the exact same formulas as DeepseekV32IndexerCache.__init__ (deepseek_v2.py lines 702-708) instead of constructing a real DeepseekV32IndexerCache and letting it self-populate these fields. If the production formula changes, this test helper could silently diverge and mask a regression.

🤖 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 `@tests/models/test_dcp_shard_draft_defaults.py` around lines 47 - 58, The test
helper _get_indexer_spec should stop assigning dcp_shard_count, dcp_replicated,
and dcp_kv_shard_count from duplicated formulas. Construct or obtain a real
DeepseekV32IndexerCache through its normal initialization path, passing the
layer and config inputs it requires, then use its self-populated fields when
calling get_kv_cache_spec.
vllm/model_executor/models/deepseek_v2.py (1)

675-685: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the unused DCP replication helper.

_replicate_indexer_cache_under_dcp is only referenced by the test import/calls, while DeepseekV32IndexerCache computes dcp_replicated directly in __init__. Since there are no production call sites, this helper is redundant and can be removed.

🤖 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 675 - 685, Remove the
unused _replicate_indexer_cache_under_dcp helper, including its definition and
any test imports or calls; preserve the existing direct dcp_replicated
computation in DeepseekV32IndexerCache.__init__.
vllm/v1/core/block_pool.py (1)

289-294: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

assert get_group_id(...) != kv_cache_group_id turns an otherwise idempotent re-cache into a crash.

_insert_block_hash already no-ops when the key matches, so a repeated cache_full_blocks call for the same boundary from the same group is harmless today; this assert makes it fatal. Relaxing it to allow the same group id (while keeping the boundary/hash checks) keeps the lockstep invariant without adding a new failure mode.

🤖 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/v1/core/block_pool.py` around lines 289 - 294, Update the existing-group
assertion in _insert_block_hash to allow get_group_id(blk.block_hash) to equal
kv_cache_group_id, while retaining the block token-count and hash equality
assertions. Preserve idempotent repeated cache_full_blocks behavior without
weakening those boundary/hash checks.
vllm/v1/core/kv_cache_utils.py (2)

1339-1347: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Prefer failing loudly over silently disabling lockstep on non-int world sizes.

isinstance(..., int) rejects e.g. numpy.int64, which would silently route allocation down the non-lockstep path instead of raising. Since the signature already types these as int, consider dropping the guard (or coercing with int(...)) so a bad caller surfaces immediately.

♻️ Suggested simplification
-    specs = list(kv_cache_specs)
-    if (
-        len(specs) < 2
-        or not isinstance(dcp_world_size, int)
-        or not isinstance(pcp_world_size, int)
-    ):
-        return False
-    cp_world_size = dcp_world_size * pcp_world_size
+    specs = list(kv_cache_specs)
+    if len(specs) < 2:
+        return False
+    cp_world_size = int(dcp_world_size) * int(pcp_world_size)
🤖 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/v1/core/kv_cache_utils.py` around lines 1339 - 1347, Update the
world-size validation in the lockstep decision logic around dcp_world_size and
pcp_world_size to remove the isinstance(..., int) checks, or explicitly coerce
the values with int(...). Preserve the existing len(specs) and cp_world_size <=
1 checks, while ensuring invalid caller values raise instead of silently
returning False.

1860-1867: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Re-implements CP-layout detection with raw getattr instead of the shared contract.

This duplicates the intent of has_nondefault_kv_cp_layout (already imported) and drifts from it: dcp_kv_shard_count == dcp * pcp is a default layout under the helper but "non-default" here. Consider plumbing the CP world sizes into this function and reusing the helper so page-size padding decisions stay consistent with grouping.

🤖 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/v1/core/kv_cache_utils.py` around lines 1860 - 1867, Update the
surrounding function to accept the CP world-size inputs required by
has_nondefault_kv_cp_layout, then replace the local is_nondefault_cp_mla raw
getattr-based detection with that shared helper for each relevant KV-cache spec.
Preserve the helper’s default-layout semantics so page-size padding matches
grouping behavior.
🤖 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/distributed/parallel_state.py`:
- Around line 2045-2065: Update the indexer shard validation before the branch
that initializes `_INDEXER_DCP` and `_INDEXER_QUERY_SPLIT`: for nonzero,
non-replicated shard counts, require a positive value that evenly divides
`decode_context_model_parallel_size`, and raise the existing topology error for
invalid values instead of falling back to the regular DCP group. Preserve the
current initialization path for valid shard counts.

In `@vllm/v1/core/kv_cache_coordinator.py`:
- Around line 303-322: The lockstep allocation path must mirror CoW replacements
rather than appending every returned block. Update the surrounding allocation
logic in the coordinator method using single_type_managers so each manager
applies CoW blocks at their replaced block_idx entries and only extends with
genuinely new blocks, preserving identical request block lengths and block IDs
across managers.

In `@vllm/v1/core/kv_cache_utils.py`:
- Around line 1727-1742: Update the non-MLA branch of the grouping key in the
custom-CP layout handling to include type(spec).__name__ alongside
spec.block_size, matching the type-aware MLA key. Keep the existing MLA key
fields and grouping behavior unchanged so different non-MLA spec types with the
same block size remain in separate buckets.

---

Nitpick comments:
In `@tests/models/test_dcp_shard_draft_defaults.py`:
- Around line 47-58: The test helper _get_indexer_spec should stop assigning
dcp_shard_count, dcp_replicated, and dcp_kv_shard_count from duplicated
formulas. Construct or obtain a real DeepseekV32IndexerCache through its normal
initialization path, passing the layer and config inputs it requires, then use
its self-populated fields when calling get_kv_cache_spec.

In `@vllm/distributed/parallel_state.py`:
- Around line 1451-1485: The new callables and tests lack the required
Google-style documentation. In vllm/distributed/parallel_state.py:1451-1485,
document get_indexer_dcp_group and get_indexer_query_split_group return values,
and document _build_indexer_replica_group_ranks arguments, returned group lists,
and its ValueError condition using Args:, Returns:, and Raises:. In
vllm/model_executor/layers/sparse_attn_indexer.py:2146-2163, document the
constructor parameters dcp_replicated and dcp_kv_shard_count. Add descriptive
docstrings to the tests in
tests/distributed/test_indexer_parallel_groups.py:9-43,
tests/model_executor/layers/test_sparse_attn_indexer_b12x.py:39-74 for
replicated-constructor behavior, and
tests/model_executor/layers/test_sparse_attn_indexer_b12x.py:452-583 for
replicated decode behavior.

In `@vllm/model_executor/models/deepseek_v2.py`:
- Around line 675-685: Remove the unused _replicate_indexer_cache_under_dcp
helper, including its definition and any test imports or calls; preserve the
existing direct dcp_replicated computation in DeepseekV32IndexerCache.__init__.

In `@vllm/v1/core/block_pool.py`:
- Around line 289-294: Update the existing-group assertion in _insert_block_hash
to allow get_group_id(blk.block_hash) to equal kv_cache_group_id, while
retaining the block token-count and hash equality assertions. Preserve
idempotent repeated cache_full_blocks behavior without weakening those
boundary/hash checks.

In `@vllm/v1/core/kv_cache_utils.py`:
- Around line 1339-1347: Update the world-size validation in the lockstep
decision logic around dcp_world_size and pcp_world_size to remove the
isinstance(..., int) checks, or explicitly coerce the values with int(...).
Preserve the existing len(specs) and cp_world_size <= 1 checks, while ensuring
invalid caller values raise instead of silently returning False.
- Around line 1860-1867: Update the surrounding function to accept the CP
world-size inputs required by has_nondefault_kv_cp_layout, then replace the
local is_nondefault_cp_mla raw getattr-based detection with that shared helper
for each relevant KV-cache spec. Preserve the helper’s default-layout semantics
so page-size padding matches grouping behavior.
🪄 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: 9c08b96b-9cc8-4b79-b0b7-a35a74191a61

📥 Commits

Reviewing files that changed from the base of the PR and between 89b4a98 and e7eded5.

📒 Files selected for processing (20)
  • tests/distributed/test_indexer_parallel_groups.py
  • tests/model_executor/layers/test_sparse_attn_indexer_b12x.py
  • tests/models/test_dcp_shard_draft_defaults.py
  • tests/v1/attention/test_indexer_dcp_localize.py
  • tests/v1/core/test_kv_cache_utils.py
  • tests/v1/core/test_prefix_caching.py
  • tests/v1/worker/test_attn_utils.py
  • tests/v1/worker/test_gpu_block_table.py
  • vllm/distributed/parallel_state.py
  • vllm/envs.py
  • vllm/model_executor/layers/sparse_attn_indexer.py
  • vllm/model_executor/models/deepseek_v2.py
  • vllm/v1/attention/backends/mla/indexer.py
  • vllm/v1/core/block_pool.py
  • vllm/v1/core/kv_cache_coordinator.py
  • vllm/v1/core/kv_cache_utils.py
  • vllm/v1/core/single_type_kv_cache_manager.py
  • vllm/v1/kv_cache_interface.py
  • vllm/v1/worker/gpu/block_table.py
  • vllm/v1/worker/gpu/model_runner.py

Comment thread vllm/distributed/parallel_state.py
Comment thread vllm/v1/core/kv_cache_coordinator.py
Comment thread vllm/v1/core/kv_cache_utils.py
@voipmonitor

voipmonitor commented Jul 25, 2026

Copy link
Copy Markdown
Author

Follow-up validation added in 8b7ca3a84f:

  • Fixes mixed target/draft topology under MTP: a partially replicated target indexer now selects its partial DCP group, while the fully sharded draft indexer selects the configured full DCP group by its actual KV shard count.
  • Preserves the existing policy that only target indexer caches are partially replicated by default.
  • Fails closed when no process group matches the indexer's shard count.
  • Unit coverage: 74 passed, 17 skipped across shard-policy, group-selection, sparse-indexer, and metadata-localization suites.
  • E2E: TP8/DCP4, target indexer 2x2 partial, draft fully DCP-sharded, MTP3 boots and sustains 134.83 / 130.30 / 126.66 tok/s at ctx0 / 64k / 400k.

No owner-merge or selected-record CKV code was added to this PR.

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (1)
vllm/distributed/parallel_state.py (1)

1454-1460: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add Google-style API sections to both selector docstrings.

These public functions accept arguments, return coordinators, and can raise RuntimeError, but their docstrings omit Args:, Returns:, and Raises:.

Proposed documentation update
 def get_indexer_dcp_group(
     expected_world_size: int | None = None,
 ) -> GroupCoordinator:
     """Return the group matching an indexer's actual KV shard count.
 
+    Args:
+        expected_world_size: The indexer's resolved KV shard count.
+
+    Returns:
+        The matching dedicated indexer or configured DCP group.
+
+    Raises:
+        RuntimeError: If no group matches the requested shard count.
+
     A partially replicated target indexer and a fully sharded speculative
     indexer can coexist in the same model. Callers that know their shard count
     must provide it so the speculative indexer does not inherit the target's
     smaller process group.
     """

As per coding guidelines, Python docstrings must use Google-style Args:, Returns:, and Raises: sections.

Also applies to: 1485-1485

🤖 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/distributed/parallel_state.py` around lines 1454 - 1460, Update both
public indexer-group selector docstrings to use Google-style sections: document
every argument under Args:, describe the returned coordinator under Returns:,
and state the RuntimeError conditions under Raises:. Keep the existing behavior
and explanatory descriptions unchanged.

Source: Coding guidelines

🤖 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.

Nitpick comments:
In `@vllm/distributed/parallel_state.py`:
- Around line 1454-1460: Update both public indexer-group selector docstrings to
use Google-style sections: document every argument under Args:, describe the
returned coordinator under Returns:, and state the RuntimeError conditions under
Raises:. Keep the existing behavior and explanatory descriptions unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e4e74ba1-a8dc-44ab-9433-5b54e04c2ee9

📥 Commits

Reviewing files that changed from the base of the PR and between e7eded5 and 8b7ca3a.

📒 Files selected for processing (5)
  • tests/distributed/test_indexer_parallel_groups.py
  • tests/model_executor/layers/test_sparse_attn_indexer_b12x.py
  • vllm/distributed/parallel_state.py
  • vllm/model_executor/layers/sparse_attn_indexer.py
  • vllm/v1/attention/backends/mla/indexer.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • tests/model_executor/layers/test_sparse_attn_indexer_b12x.py
  • vllm/v1/attention/backends/mla/indexer.py
  • vllm/model_executor/layers/sparse_attn_indexer.py

@voipmonitor

Copy link
Copy Markdown
Author

Addressed the three actionable review findings in bc0c624b51:

  • Invalid VLLM_DCP_INDEXER_SHARDS values now fail during model-parallel initialization instead of silently falling back to the full-DCP topology.
  • Lockstep MLA partial-prefix CoW now replaces the shared tail block in every cache group with one common destination block while scheduling exactly one physical copy.
  • Non-MLA custom-CP specs are grouped by concrete spec type as well as block size, preventing incompatible layouts from being unified.

Validation:

  • ruff format / ruff check: clean
  • prefix-cache suites: 105 passed
  • DCP/indexer suites: 82 passed, 17 skipped
  • relevant KV-layout tests: 6 passed

The broader test_kv_cache_utils.py source-mount run reached unrelated device-detection tests that require the image's compiled extension to be imported from its installed package; the changed KV-layout subset is fully covered above.

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (1)
vllm/v1/core/single_type_kv_cache_manager.py (1)

390-414: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add Args/Returns sections to the new CoW docstrings.

pending_cow_block_index and apply_lockstep_cow take parameters (request_id, block_idx, cow_block) but their docstrings omit Args:/Returns: sections, unlike every other documented method in this class (add_local_computed_blocks, allocate_new_blocks, get_num_blocks_to_allocate, etc.).

📝 Suggested docstring additions
     def pending_cow_block_index(self, request_id: str) -> int | None:
-        """Return the pending partial-hit CoW index without consuming it."""
+        """Return the pending partial-hit CoW index without consuming it.
+
+        Args:
+            request_id: The request ID.
+
+        Returns:
+            The pending block index, or ``None`` if there is no pending
+            partial-hit CoW for this request.
+        """
         """Mirror a peer manager's CoW substitution using the same block ID.

         The primary manager owns the physical copy operation. A lockstep peer
         only transfers its request reference from the shared source block to
         the already allocated destination block.
+
+        Args:
+            request_id: The request ID.
+            block_idx: The block index being substituted, must match this
+                manager's own pending CoW index for the request.
+            cow_block: The destination block allocated by the primary
+                manager for this lockstep group.
         """

As per coding guidelines, **/*.py code should "Use Google-style docstrings in Python code, with Args:/Returns:/Raises: sections instead of reStructuredText/Sphinx fields such as :param:, :return:, and :rtype:."

🤖 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/v1/core/single_type_kv_cache_manager.py` around lines 390 - 414, Add
Google-style Args and Returns sections to the docstrings of
pending_cow_block_index and apply_lockstep_cow, documenting each parameter and
the returned value where applicable. Keep the existing behavior and descriptions
unchanged, and follow the class’s established docstring style.

Source: Coding guidelines

🤖 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.

Nitpick comments:
In `@vllm/v1/core/single_type_kv_cache_manager.py`:
- Around line 390-414: Add Google-style Args and Returns sections to the
docstrings of pending_cow_block_index and apply_lockstep_cow, documenting each
parameter and the returned value where applicable. Keep the existing behavior
and descriptions unchanged, and follow the class’s established docstring style.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e90372d8-2ed9-4444-b861-3b2f7794d5cf

📥 Commits

Reviewing files that changed from the base of the PR and between 8b7ca3a and 1f2a1dd.

📒 Files selected for processing (8)
  • tests/distributed/test_indexer_parallel_groups.py
  • tests/v1/attention/test_indexer_dcp_localize.py
  • tests/v1/core/test_kv_cache_utils.py
  • tests/v1/core/test_prefix_caching.py
  • vllm/distributed/parallel_state.py
  • vllm/v1/core/kv_cache_coordinator.py
  • vllm/v1/core/kv_cache_utils.py
  • vllm/v1/core/single_type_kv_cache_manager.py
🚧 Files skipped from review as they are similar to previous changes (4)
  • vllm/distributed/parallel_state.py
  • vllm/v1/core/kv_cache_coordinator.py
  • tests/v1/attention/test_indexer_dcp_localize.py
  • vllm/v1/core/kv_cache_utils.py

@voipmonitor
voipmonitor force-pushed the perf/gg-dcp-partial-indexer-topology-20260725 branch from a9e5b90 to 673733e Compare July 27, 2026 22:49

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
vllm/v1/attention/backends/mla/indexer.py (1)

1191-1196: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Derive the CPU decode bound using the indexer shard group.

Line 1193 reuses dcp_local_seq_lens_cpu, which belongs to the main CKV cache's configured DCP layout. With DCP4 and a partially replicated indexer using two shards, this can understate the indexer’s local maximum versus self.dcp_world_size/self.dcp_rank, causing B12X top-k to scan too small a KV window. Derive the CPU bound with the indexer group’s geometry (or safely use active_width_tokens) and add a DCP4/indexer-shards=2 decode test.

🤖 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/v1/attention/backends/mla/indexer.py` around lines 1191 - 1196, Update
the decode CPU bound in the surrounding indexer metadata construction to use the
indexer shard group’s DCP geometry, based on its world size and rank, rather
than slicing dcp_local_seq_lens_cpu from the main CKV layout; alternatively use
active_width_tokens when it safely represents the bound. Preserve the None
behavior when local DCP metadata is unavailable, and add coverage for DCP4 with
two indexer shards verifying the B12X top-k scan uses the full local KV window.
🧹 Nitpick comments (3)
vllm/model_executor/layers/sparse_attn_indexer.py (2)

1170-1183: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

getattr-based probing for get_indexer_dcp_group is dead defensiveness.

The module already imports get_indexer_dcp_group unconditionally at line 14, so the selector can never be None and the get_dcp_group() fallback is unreachable. get_indexer_dcp_group also already fails closed on a shard-count mismatch, making the extra check redundant. Calling it directly keeps this helper consistent with the rest of the file. Same pattern at lines 1772-1777 for the query-split selector.

♻️ Suggested simplification
-    from vllm.distributed import parallel_state
-
-    selector = getattr(parallel_state, "get_indexer_dcp_group", None)
-    group = (
-        selector(expected_world_size)
-        if selector is not None
-        else parallel_state.get_dcp_group()
-    )
-    if int(group.world_size) != int(expected_world_size):
-        raise RuntimeError(
-            "DCP owner top-k group does not match the indexer KV shard count: "
-            f"group={group.world_size}, shards={expected_world_size}"
-        )
-    return group
+    return get_indexer_dcp_group(int(expected_world_size))
🤖 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/layers/sparse_attn_indexer.py` around lines 1170 - 1183,
Update the DCP group helper to call the already imported get_indexer_dcp_group
directly with expected_world_size, removing getattr probing, the get_dcp_group
fallback, and the redundant world-size validation. Apply the same simplification
to the query-split selector path around the corresponding helper.

446-478: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Silent except Exception: pass now hides a real misconfiguration.

With expected_dcp_world_size supplied, get_indexer_dcp_group(group_world_size) raises when no group matches the requested shard count (fail-closed contract in parallel_state.get_indexer_dcp_group). Swallowing it here leaves dcp_world_size = expected with dcp_rank = 0 on every rank, so warmup silently exercises the wrong rank geometry instead of surfacing the topology error. At minimum log at debug so it is diagnosable; ruff also flags both blind handlers (S110/BLE001).

♻️ Suggested change
-    except Exception:
-        pass
+    except Exception:
+        logger.debug("Failed to read DCP warmup config", exc_info=True)
 
     try:
         from vllm.distributed.parallel_state import get_indexer_dcp_group
 
         dcp_group = get_indexer_dcp_group(group_world_size)
         if int(dcp_group.world_size) > 1:
             dcp_world_size = int(dcp_group.world_size)
             dcp_rank = int(dcp_group.rank_in_group)
-    except Exception:
-        pass
+    except Exception:
+        logger.debug(
+            "No indexer DCP group for world size %s; using rank 0 for warmup",
+            group_world_size,
+            exc_info=True,
+        )
🤖 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/layers/sparse_attn_indexer.py` around lines 446 - 478,
Update _get_dcp_warmup_params to stop using blind except Exception: pass
handlers. Preserve the fallback behavior only for genuinely unavailable optional
configuration, but log caught exceptions at debug level so topology and
configuration failures are diagnosable; ensure the get_indexer_dcp_group failure
with an explicitly supplied expected_dcp_world_size is not silently hidden and
satisfies Ruff’s S110/BLE001 checks.

Source: Linters/SAST tools

tests/v1/core/test_prefix_caching.py (1)

180-217: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider adding a partial-replication (dcp_kv_shard_count) lockstep coordinator test.

All lockstep MLA tests in this file exercise full replication (dcp_replicated=True) for the indexer group. The PR's headline feature — partial DCP replication via dcp_kv_shard_count (e.g. DCP8 with a 4-shard indexer) — is only exercised at the grouping-utility level in tests/v1/core/test_kv_cache_utils.py (test_partial_replicated_mla_uses_lockstep_dcp8_layout), not through the actual KVCacheManager/coordinator allocation path (block-size equality assert, CoW mirroring, slot sharing). A variant of make_lockstep_mla_manager using dcp_kv_shard_count instead of dcp_replicated=True would close this gap.

🤖 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 `@tests/v1/core/test_prefix_caching.py` around lines 180 - 217, Add a
partial-replication lockstep coordinator test alongside
make_lockstep_mla_manager, configuring the indexer MLAAttentionSpec with
dcp_kv_shard_count=4 instead of dcp_replicated=True while retaining the DCP
world size and lockstep block-size setup. Exercise the KVCacheManager allocation
path, including block-size validation, copy-on-write mirroring, and shared
slots, rather than relying only on grouping utilities.
🤖 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/v1/core/kv_cache_utils.py`:
- Around line 652-664: Update resolve_kv_cache_block_sizes to apply
get_kv_cache_cp_shard_count to wrapped attention specifications, including
UniformTypeKVCacheSpecs groups produced by lockstep_mla_layout, rather than
scaling only direct AttentionSpec instances. Use each spec’s dcp_replicated and
dcp_kv_shard_count properties and preserve raw block_size handling for unrelated
spec types.
- Around line 1687-1698: Update the `_is_lockstep_mla_spec_layout` input
filtering so it evaluates only MLA-family specifications, including
`MLAAttentionSpec`, `SlidingWindowMLASpec`, and other indexer/replicated-MLA
variants, while excluding unrelated KV-cache specs. Preserve the existing
lockstep layout logic and ensure `lockstep_mla_layout` is computed from this
filtered subset before `has_custom_cp` is evaluated.

---

Outside diff comments:
In `@vllm/v1/attention/backends/mla/indexer.py`:
- Around line 1191-1196: Update the decode CPU bound in the surrounding indexer
metadata construction to use the indexer shard group’s DCP geometry, based on
its world size and rank, rather than slicing dcp_local_seq_lens_cpu from the
main CKV layout; alternatively use active_width_tokens when it safely represents
the bound. Preserve the None behavior when local DCP metadata is unavailable,
and add coverage for DCP4 with two indexer shards verifying the B12X top-k scan
uses the full local KV window.

---

Nitpick comments:
In `@tests/v1/core/test_prefix_caching.py`:
- Around line 180-217: Add a partial-replication lockstep coordinator test
alongside make_lockstep_mla_manager, configuring the indexer MLAAttentionSpec
with dcp_kv_shard_count=4 instead of dcp_replicated=True while retaining the DCP
world size and lockstep block-size setup. Exercise the KVCacheManager allocation
path, including block-size validation, copy-on-write mirroring, and shared
slots, rather than relying only on grouping utilities.

In `@vllm/model_executor/layers/sparse_attn_indexer.py`:
- Around line 1170-1183: Update the DCP group helper to call the already
imported get_indexer_dcp_group directly with expected_world_size, removing
getattr probing, the get_dcp_group fallback, and the redundant world-size
validation. Apply the same simplification to the query-split selector path
around the corresponding helper.
- Around line 446-478: Update _get_dcp_warmup_params to stop using blind except
Exception: pass handlers. Preserve the fallback behavior only for genuinely
unavailable optional configuration, but log caught exceptions at debug level so
topology and configuration failures are diagnosable; ensure the
get_indexer_dcp_group failure with an explicitly supplied
expected_dcp_world_size is not silently hidden and satisfies Ruff’s S110/BLE001
checks.
🪄 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: ff218887-3cf6-4d20-b4d4-e7e1913ae893

📥 Commits

Reviewing files that changed from the base of the PR and between 1f2a1dd and 673733e.

📒 Files selected for processing (18)
  • tests/distributed/test_indexer_parallel_groups.py
  • tests/model_executor/layers/test_sparse_attn_indexer_b12x.py
  • tests/models/test_dcp_shard_draft_defaults.py
  • tests/v1/attention/test_indexer_dcp_localize.py
  • tests/v1/core/test_kv_cache_utils.py
  • tests/v1/core/test_prefix_caching.py
  • tests/v1/worker/test_gpu_block_table.py
  • vllm/distributed/parallel_state.py
  • vllm/envs.py
  • vllm/model_executor/layers/sparse_attn_indexer.py
  • vllm/model_executor/models/deepseek_v2.py
  • vllm/v1/attention/backends/mla/indexer.py
  • vllm/v1/core/kv_cache_coordinator.py
  • vllm/v1/core/kv_cache_utils.py
  • vllm/v1/core/single_type_kv_cache_manager.py
  • vllm/v1/kv_cache_interface.py
  • vllm/v1/worker/gpu/block_table.py
  • vllm/v1/worker/gpu/model_runner.py
🚧 Files skipped from review as they are similar to previous changes (8)
  • vllm/v1/worker/gpu/block_table.py
  • tests/distributed/test_indexer_parallel_groups.py
  • vllm/distributed/parallel_state.py
  • vllm/v1/kv_cache_interface.py
  • vllm/v1/core/single_type_kv_cache_manager.py
  • tests/v1/attention/test_indexer_dcp_localize.py
  • vllm/model_executor/models/deepseek_v2.py
  • tests/models/test_dcp_shard_draft_defaults.py

Comment thread vllm/v1/core/kv_cache_utils.py Outdated
Comment thread vllm/v1/core/kv_cache_utils.py

@coderabbitai coderabbitai 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.

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 `@tests/v1/core/test_kv_cache_utils.py`:
- Around line 1801-1805: Update the test around
_get_kv_cache_groups_uniform_groups to assert that grouped_specs contains both
target and indexer groups before resolving or validating the result, ensuring
the multi-group LCM/GCD path is exercised rather than the single-group fast
path.
🪄 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: ed2d9eb1-92f8-46c3-8289-e2b7f48bbd85

📥 Commits

Reviewing files that changed from the base of the PR and between 673733e and ef60ee7.

📒 Files selected for processing (2)
  • tests/v1/core/test_kv_cache_utils.py
  • vllm/v1/core/kv_cache_utils.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • vllm/v1/core/kv_cache_utils.py

Comment thread tests/v1/core/test_kv_cache_utils.py
@lukealonso
lukealonso merged commit 7847562 into local-inference-lab:dev/gilded-gnosis Jul 29, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants