Skip to content

[GG] perf(indexer): shard sparse prefill queries and halve result traffic - #175

Merged
lukealonso merged 1 commit into
local-inference-lab:dev/gilded-gnosisfrom
voipmonitor:perf/gg-query-split-gather-20260724
Jul 30, 2026
Merged

[GG] perf(indexer): shard sparse prefill queries and halve result traffic#175
lukealonso merged 1 commit into
local-inference-lab:dev/gilded-gnosisfrom
voipmonitor:perf/gg-query-split-gather-20260724

Conversation

@voipmonitor

@voipmonitor voipmonitor commented Jul 24, 2026

Copy link
Copy Markdown

Summary

  • shard sparse-indexer query rows across the query-split group;
  • gather only the final contiguous int32 top-k indices directly into the caller-owned buffer;
  • stop gathering FP32 scores after the DCP-local candidate merge, because sparse attention does not consume them;
  • support the same exact query sharding at DCP1, where TP ranks otherwise repeat identical indexer work;
  • keep the PyNCCL path in-place while giving generic torch.distributed backends a safe copied send slice.

The feature remains gated by VLLM_DCP_QUERY_SPLIT. This PR does not globally enable it; deployment policy retains an explicit kill switch and excludes unqualified virtual-TP6 layouts.

Current GG integration

The PR was reconstructed on 2026-07-30 from current dev/gilded-gnosis@2d9c9801094842960b94c053539d62892e35a0bc.

The earlier branch carried its own query-split topology helper. Current GG now has canonical full and partially replicated indexer topology through _build_indexer_replica_group_ranks, get_indexer_dcp_group, and get_indexer_query_split_group. This refresh removes the duplicate implementation and uses that canonical builder for DCP1, partial DCP, and full DCP.

The remaining diff is therefore limited to the still-missing communication optimization, DCP1 group initialization, and focused regression coverage. It composes with current GG's partial-indexer groups and context-crossover policy instead of replacing either one.

Why this is exact

Each query-split rank computes a disjoint row slice with the existing sparse top-k kernel, then all-gathers the resulting int32 indices. It does not compress scores, indices, CKV, weights, or activations. A deterministic unique 64k prompt produced the same winning token; baseline-vs-query logprob delta was 7.15e-7 versus 9.54e-7 baseline repeat spread.

Communication impact

For TP8/DCP4, 8192 rows, top-k 2048, and query-split size 2, eliminating the FP32 score collective reduces result traffic per GPU/layer/direction from 64 MiB to 32 MiB. The isolated collective median fell from 2.726 ms to 1.412 ms per active layer and 8k chunk.

Qualified 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%
TP8/DCP4 NVFP4 A16 64k 5,362.5 5,484.5 tok/s +2.28%

The immutable v20 r14 image contains the earlier, E2E-qualified implementation at 2c14a58a21c724f08d01d988b9972d10c77ed4c0. This refresh preserves that transport behavior while replacing obsolete topology code with current GG's implementation.

Virtual TP6 has 11 padded heads per rank and remains excluded from auto-enable:

TP6 topology Context Disabled Enabled Delta
DCP1 64k 5,014 4,959 -1.1%
DCP2 64k 3,851 3,830 -0.6%
DCP3 64k / 400k 3,354 / 3,123 2,822 / 2,663 -15.9% / -14.7%
DCP6 64k / 400k 2,306 / 2,238.8 2,288 / 2,236.7 -0.8% / -0.1%

Validation

Current-GG refresh:

  • 47/47 focused tests pass;
  • DCP1, partial-DCP, full-DCP, selector, PyNCCL in-place, generic fallback, alias, and score-preservation contracts are covered;
  • Ruff check and format pass;
  • git diff --check passes;
  • the branch is one commit over current GG.

Earlier E2E qualification retained by the unchanged transport core:

  • TP8/DCP1 64k and 400k;
  • TP4/DCP1 64k same-GPU A/B;
  • TP6 DCP1/2/3/6 64k and long-context ON/OFF;
  • TP8/DCP4 two-run A/B.

Summary by CodeRabbit

  • New Features

    • Added support for partially replicated sparse-indexer decode-context and query-split configurations.
    • Improved sparse-indexer data gathering and merging across distributed layouts.
    • Added configurable replication and KV shard-count options for sparse-indexer initialization.
  • Bug Fixes

    • Improved handling of overlapping buffers during distributed gathers.
    • Added validation for index buffer aliasing and compatible shard configurations.
  • Tests

    • Expanded coverage for distributed group layouts, in-place gathering, fallback behavior, and invalid configurations.

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

The PR adds indexer-specific DCP and query-split group topology, introduces alias-safe in-place index gathering, updates B12X prefill integration, and adds coverage for group layouts and collective buffer contracts.

Indexer DCP and query-split integration

Layer / File(s) Summary
Indexer group topology
vllm/distributed/parallel_state.py, tests/distributed/test_indexer_parallel_groups.py
Indexer DCP/query-split groups are selected and built from TP rank layouts, shard-count validation is added, and partial/full configurations are tested.
In-place query-split index gathering
vllm/model_executor/layers/sparse_attn_indexer.py, tests/model_executor/layers/test_sparse_attn_indexer_b12x.py
Collective helpers validate contiguous, aliased buffers and clone overlapping inputs for the torch.distributed fallback; tests cover writes, alias handling, unchanged scores, and invalid aliasing.
B12X prefill integration
vllm/model_executor/layers/sparse_attn_indexer.py
The query-split prefill path gathers indices into shared storage and no longer gathers top-k scores.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant initialize_model_parallel
  participant _build_indexer_replica_group_ranks
  participant _query_split_all_gather_indices
  participant _dcp_all_gather_first_dim_into
  participant B12X_prefill
  initialize_model_parallel->>_build_indexer_replica_group_ranks: build DCP and query-split rank groups
  _build_indexer_replica_group_ranks-->>initialize_model_parallel: return rank layouts
  B12X_prefill->>_query_split_all_gather_indices: submit local and gathered index buffers
  _query_split_all_gather_indices->>_dcp_all_gather_first_dim_into: gather aliased indices
  _dcp_all_gather_first_dim_into-->>B12X_prefill: restore gathered indices
Loading

Possibly related issues

  • local-inference-lab/rtx6kpro#36 — Covers the same GLM-5.2 DCP sparse-attention query sharding, indexer topology, and top-k data movement areas.

Possibly related PRs

Suggested reviewers: lukealonso, koush

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: sharding sparse prefill queries to reduce result traffic.
✨ 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.

🧹 Nitpick comments (2)
vllm/model_executor/layers/sparse_attn_indexer.py (1)

399-402: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Cover the non-PyNCCL aliasing path.

The added test exercises only pynccl_comm.all_gather; the fallback passes an aliased rank-local view to torch.distributed.all_gather_into_tensor. Add backend-level coverage or verify the deployed PyTorch/NCCL combination supports this exact in-place layout before relying on it. NCCL documents the rank-local condition as sendbuff == recvbuff + rank * sendcount, while PyTorch’s API documentation specifies sizing but not this aliasing guarantee. (github.com)

🤖 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 399 - 402,
Update _dcp_all_gather_first_dim_into to cover or validate the aliased
rank-local send-buffer path used by torch.distributed.all_gather_into_tensor,
not only pynccl_comm.all_gather. Add backend-level coverage for the exact
in-place layout, or establish an explicit PyTorch/NCCL capability check and
provide a safe non-aliased fallback when unsupported.
vllm/distributed/parallel_state.py (1)

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

Document both new helpers with Google-style sections.

Both helpers accept arguments and raise validation errors, but their docstrings contain only a summary.

  • vllm/distributed/parallel_state.py#L1450-L1453: add Args:, Returns:, and Raises: sections describing the TP cohorts, DCP size, output groups, and ValueError.
  • vllm/model_executor/layers/sparse_attn_indexer.py#L375-L380: add Args:, Returns:, and Raises: sections describing the group, buffers, and validation failures.
🤖 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 - 1453, Document both
helpers with Google-style Args, Returns, and Raises sections: in
vllm/distributed/parallel_state.py lines 1450-1453, update
_get_query_split_group_ranks to describe TP cohorts, dcp_size, transposed output
groups, and ValueError validation; in
vllm/model_executor/layers/sparse_attn_indexer.py lines 375-380, update the
corresponding helper’s docstring to describe its group and buffers, return
value, and validation failures.

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-1453: Document both helpers with Google-style Args, Returns,
and Raises sections: in vllm/distributed/parallel_state.py lines 1450-1453,
update _get_query_split_group_ranks to describe TP cohorts, dcp_size, transposed
output groups, and ValueError validation; in
vllm/model_executor/layers/sparse_attn_indexer.py lines 375-380, update the
corresponding helper’s docstring to describe its group and buffers, return
value, and validation failures.

In `@vllm/model_executor/layers/sparse_attn_indexer.py`:
- Around line 399-402: Update _dcp_all_gather_first_dim_into to cover or
validate the aliased rank-local send-buffer path used by
torch.distributed.all_gather_into_tensor, not only pynccl_comm.all_gather. Add
backend-level coverage for the exact in-place layout, or establish an explicit
PyTorch/NCCL capability check and provide a safe non-aliased fallback when
unsupported.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 0d9320bf-f356-453b-9328-050d15b6c3fc

📥 Commits

Reviewing files that changed from the base of the PR and between 4a4299c and 6cf014d.

📒 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

@voipmonitor
voipmonitor force-pushed the perf/gg-query-split-gather-20260724 branch 2 times, most recently from f8c0cb4 to 599d3f6 Compare July 24, 2026 22:37
@voipmonitor voipmonitor changed the title [GG] perf(dcp): halve query-split result traffic [GG] perf(indexer): shard sparse prefill queries and halve result traffic Jul 24, 2026
@voipmonitor

Copy link
Copy Markdown
Author

Addressed the review findings while rebasing onto current dev/gilded-gnosis:

  • the PyNCCL path keeps the validated zero-copy NCCL alias contract
  • the generic torch.distributed fallback now detects overlapping send/receive storage and clones only the send slice before all_gather_into_tensor
  • added a forced fallback regression test and Google-style helper contracts
  • expanded the same coherent query-split path to DCP1 after TP8/TP4 E2E qualification; TP6 remains launcher-disabled

Validation: 27/27 targeted tests, Ruff, and git diff --check.

@voipmonitor
voipmonitor force-pushed the perf/gg-query-split-gather-20260724 branch from 2c14a58 to a563e57 Compare July 30, 2026 22:01
@voipmonitor

Copy link
Copy Markdown
Author

Refreshed this PR onto current dev/gilded-gnosis@2d9c9801094842960b94c053539d62892e35a0bc. The branch now reuses GG’s canonical full/partial indexer topology and contains one commit with only the still-missing index-only in-place gather, DCP1 initialization, generic-backend alias fallback, and focused tests. Validation: 47/47 targeted tests, Ruff check/format, and git diff --check.

@lukealonso
lukealonso merged commit 443e1d6 into local-inference-lab:dev/gilded-gnosis Jul 30, 2026
1 of 3 checks passed

@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/model_executor/layers/sparse_attn_indexer.py (1)

504-536: 🩺 Stability & Availability | 🔵 Trivial

Silent exception swallowing flagged by static analysis.

Both try/except Exception: pass blocks here (including the one spanning the changed lines 511-524) silently discard any failure resolving vllm_config/parallel_config or the indexer DCP group, which could mask real misconfiguration during warmup instead of surfacing it. Ruff flags this pattern (S110/BLE001).

♻️ Suggested fix: log instead of silently swallowing
-    except Exception:
-        pass
+    except Exception:
+        logger.debug("Failed to resolve DCP warmup config", 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 504 - 536,
Replace both silent `except Exception: pass` handlers in
`_get_dcp_warmup_params` with appropriate warning or debug logging that records
the failure and preserves the existing fallback values. Keep the warmup
parameter resolution flow unchanged while ensuring failures from
`get_current_vllm_config_or_none`/`parallel_config` and `get_indexer_dcp_group`
are no longer discarded and satisfy the static-analysis rule.

Source: Linters/SAST tools

🤖 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/model_executor/layers/sparse_attn_indexer.py`:
- Around line 504-536: Replace both silent `except Exception: pass` handlers in
`_get_dcp_warmup_params` with appropriate warning or debug logging that records
the failure and preserves the existing fallback values. Keep the warmup
parameter resolution flow unchanged while ensuring failures from
`get_current_vllm_config_or_none`/`parallel_config` and `get_indexer_dcp_group`
are no longer discarded and satisfy the static-analysis rule.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 805274a8-6a23-4976-8166-029dc79583d8

📥 Commits

Reviewing files that changed from the base of the PR and between 6cf014d and a563e57.

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

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