Skip to content

[GG] perf(indexer): shard DCP1 prefill queries across TP#176

Closed
voipmonitor wants to merge 2 commits into
dev/gilded-gnosisfrom
perf/gg-dcp1-query-split-20260724
Closed

[GG] perf(indexer): shard DCP1 prefill queries across TP#176
voipmonitor wants to merge 2 commits into
dev/gilded-gnosisfrom
perf/gg-dcp1-query-split-20260724

Conversation

@voipmonitor

@voipmonitor voipmonitor commented Jul 24, 2026

Copy link
Copy Markdown

Summary

  • allow the existing exact sparse-indexer query split when DCP=1, where all TP ranks otherwise repeat identical indexer work
  • gather only the final contiguous int32 top-k indices in place; sparse attention does not consume the per-rank scores, so gathering scores doubled traffic
  • retain the existing opt-in environment gate; deployment policy can keep unsupported or slower topologies disabled

Why this is exact

Each TP rank owns a disjoint query-row slice, computes the same sparse top-k operation it computed before, and all-gathers the resulting int32 indices. No score, index, CKV, or activation compression is introduced. A deterministic unique 64k prompt produced the same winning token, with a baseline-vs-query logprob difference of 7.15e-7 versus 9.54e-7 baseline repeat spread.

E2E results

GLM-5.2, MTP off, exact raw prefill, no concurrent model loading or benchmark traffic:

Topology Context Before After Delta
TP8/DCP1 NVFP4 A16 400k 4,403 5,805 tok/s +31.8%
TP8/DCP1 NVFP4 A16 64k ~5,860 ~6,148 tok/s +4.9%
TP4/DCP1 NF3 hybrid, same GPUs 0-3 64k 4,125 4,226 tok/s +2.4%

Virtual TP6 has 11 padded heads per rank and did not benefit: DCP1 regressed about 1.1%, DCP3 regressed 15.9% at 64k and 14.7% at 400k, and DCP6 was neutral at 400k. The image helper therefore keeps this opt-in path auto-disabled for all TP6 modes.

Tests

  • tests/distributed/test_query_split_groups.py
  • tests/model_executor/layers/test_sparse_attn_indexer_b12x.py
  • 26 passed in the pinned v20 runtime
  • TP8/DCP1 64k and 400k E2E
  • TP4/DCP1 64k same-GPU A/B
  • TP6 DCP1/2/3/6 64k and long-context ON/OFF validation

Summary by CodeRabbit

  • Bug Fixes

    • Improved distributed query-split rank handling across different parallel configurations, including single-device context parallelism.
    • Added validation for invalid or incompatible query-split sizes with clear errors.
    • Improved sparse-attention index gathering to preserve shared buffers correctly and avoid unnecessary score transfers.
    • Added safeguards for non-contiguous or improperly aliased index buffers.
  • Tests

    • Expanded coverage for query-split layouts, input validation, and sparse-attention gathering behavior.

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Query-split rank construction now uses a validated DCP layout helper. B12X sparse-attention prefill gathers top-k indices directly into shared storage through an alias-checked helper, without gathering scores. Tests cover rank layouts, validation errors, in-place gathering, and aliasing.

Changes

Query-split DCP flow

Layer / File(s) Summary
Query-split rank layout construction
vllm/distributed/parallel_state.py, tests/distributed/test_query_split_groups.py
Adds validated DCP/TP rank transposition, reuses TP cohort ranks during initialization, supports dcp_size == 1, and tests layouts and invalid sizes.
Aliased query-split index gathering
vllm/model_executor/layers/sparse_attn_indexer.py, tests/model_executor/layers/test_sparse_attn_indexer_b12x.py
Adds contiguity, shape, and aliasing checks before gathering indices into caller-owned storage, with tests for in-place output and rejected non-aliasing buffers.
B12X prefill index restoration
vllm/model_executor/layers/sparse_attn_indexer.py
Uses the new helper to restore gathered indices and removes score gathering from the query-split branch.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant B12XPrefill
  participant QuerySplitGroup
  participant DCPGather
  participant SharedIndexBuffer
  B12XPrefill->>QuerySplitGroup: resolve query-split group
  B12XPrefill->>DCPGather: gather top-k indices
  DCPGather->>SharedIndexBuffer: write rank-local slices in place
Loading

Possibly related PRs

Suggested reviewers: lukealonso, yewentao256

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 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 main change: sharding DCP1 prefill queries across tensor-parallel ranks for performance.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch perf/gg-dcp1-query-split-20260724

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.

@voipmonitor

Copy link
Copy Markdown
Author

Superseded by #175. During cleanup it became clear that DCP1 activation directly depends on the query-split rank-construction and exact index-gather contract introduced there. Keeping this as a second PR would either duplicate the same code or create a hidden stacked dependency. #175 now contains the cohesive implementation, current-GG rebase, review fixes, DCP1 qualification data, TP6 exclusions, and 27/27 targeted tests.

@voipmonitor
voipmonitor deleted the perf/gg-dcp1-query-split-20260724 branch July 24, 2026 22:39

@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 (2)
vllm/distributed/parallel_state.py (1)

1450-1472: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a Raises: section to the new helper's docstring.

_get_query_split_group_ranks raises ValueError for two distinct conditions (non-positive dcp_size, non-divisible TP group size) but the docstring is a bare one-liner with no Args:/Raises: sections documenting them.

📝 Proposed docstring
 def _get_query_split_group_ranks(
     tp_group_ranks: list[list[int]], dcp_size: int
 ) -> list[list[int]]:
-    """Transpose DCP groups within each independent TP cohort."""
+    """Transpose DCP groups within each independent TP cohort.
+
+    Args:
+        tp_group_ranks: Ranks belonging to each independent TP group.
+        dcp_size: Number of DCP ranks within each TP group.
+
+    Returns:
+        Query-split rank groups, one per DCP position across all TP cohorts.
+
+    Raises:
+        ValueError: If `dcp_size` is not positive, or a TP group's size is
+            not divisible by `dcp_size`.
+    """

As per coding guidelines, "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/distributed/parallel_state.py` around lines 1450 - 1472, Update the
docstring for _get_query_split_group_ranks with a Google-style Raises section
documenting ValueError for non-positive dcp_size and for TP group sizes not
divisible by dcp_size; preserve the existing summary and implementation.

Source: Coding guidelines

vllm/model_executor/layers/sparse_attn_indexer.py (1)

375-404: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add Args:/Raises: sections to the docstring.

_query_split_all_gather_indices raises three distinct RuntimeErrors (contiguity, row-count, aliasing) that aren't obvious from the one-line summary alone.

📝 Proposed docstring
 def _query_split_all_gather_indices(
     group,
     local_indices: torch.Tensor,
     gathered_indices: torch.Tensor,
 ) -> None:
-    """Restore query-split rows directly into their shared index buffer."""
+    """Restore query-split rows directly into their shared index buffer.
+
+    Args:
+        group: Query-split process group.
+        local_indices: This rank's contiguous slice of top-k indices; must
+            alias `gathered_indices` at this rank's offset.
+        gathered_indices: Shared buffer to gather all ranks' rows into.
+
+    Raises:
+        RuntimeError: If the buffers are non-contiguous, have a mismatched
+            row count/trailing shape, or `local_indices` does not alias its
+            expected slot in `gathered_indices`.
+    """

As per coding guidelines, "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/model_executor/layers/sparse_attn_indexer.py` around lines 375 - 404,
Expand the `_query_split_all_gather_indices` docstring with Google-style `Args:`
entries for `group`, `local_indices`, and `gathered_indices`, and a `Raises:`
section documenting the three `RuntimeError` conditions: non-contiguous buffers,
invalid output shapes, and failure of `local_indices` to alias its rank-local
output slice.

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 1450-1472: Update the docstring for _get_query_split_group_ranks
with a Google-style Raises section documenting ValueError for non-positive
dcp_size and for TP group sizes not divisible by dcp_size; preserve the existing
summary and implementation.

In `@vllm/model_executor/layers/sparse_attn_indexer.py`:
- Around line 375-404: Expand the `_query_split_all_gather_indices` docstring
with Google-style `Args:` entries for `group`, `local_indices`, and
`gathered_indices`, and a `Raises:` section documenting the three `RuntimeError`
conditions: non-contiguous buffers, invalid output shapes, and failure of
`local_indices` to alias its rank-local output slice.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e3f49032-420e-493f-bf81-81fc7395a48f

📥 Commits

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

📒 Files selected for processing (4)
  • tests/distributed/test_query_split_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

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.

1 participant