Repository navigation
Conversation
Every tensor-parallel rank runs the KPool indexer on the same gathered sequence with the same replicated indexer weights, so all TP ranks compute identical O(queries x pools) scores. A query's pool selection depends only on that query and the pooled keys, so the query rows can be split across the TP group: each rank scores and selects pools for a contiguous 1/TP slice of the queries (inside the existing byte-capped chunk loop), then the int32 pool IDs are all-gathered. The selected indices are the same as without sharding. Opt-in via TransformerConfig.dsa_indexer_kpool_shard_queries_across_tp (--dsa-indexer-kpool-shard-queries-across-tp). Skipped when the attention query rows are already sequence-local per TP rank. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Avigya Basnet <avigya.basnet@anyscale.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Adds an opt-in flag that splits KPool indexer scoring across the tensor-parallel group by query rows, so TP ranks stop recomputing the same O(queries x pools) scores; the selected indices are unchanged.
Follows up #7522 (by @HollowMan6), which added the KPool DSA indexer.
Mechanism
Under sequence parallelism,
DSAIndexer.forward_before_topkgathersxandqrover the TP group before projecting, and the indexer projections are duplicated across TP ranks (average_gradients_across_tp_domain; without the indexer loss they are frozen). Without sequence parallelism the activations are already replicated across TP. Either way, every TP rank callsfused_qk_topk_kpoolwith the sameq,k,weights, gate scores and mask/varlen bounds, and every rank computes the same full[batch, queries, pools]score matrix and top-k. At TP=8, seven of the eight scorings are redundant. That cost grows with the square of the sequence length, so it takes up more of the step as context grows.Each query row's pool selection depends only on that row and the pooled keys. The rows can therefore be split across ranks and the results concatenated, with every row matching the unsharded computation.
Change
fused_qk_topk_kpool(..., query_shard_group=None): when a group is passed (requiresreturn_index_scores=False), rankrscores rows[r*ceil(sq/G), min(sq, (r+1)*ceil(sq/G)))through the existing chunked path_kpool_topk_from_score_chunks. That path gainsquery_start/query_endand keeps its_KPOOL_SCORE_CHUNK_BYTES(2 GiB) cap on the score buffer per chunk. The local pool IDs are cast to int32, padded to equal length, combined with oneall_gather_into_tensor, and reassembled into the full[batch, sq, budget]tensor. Pool expansion, tail append and mask filtering then run unchanged on the full tensor, so every rank returns the same indices.TransformerConfig.dsa_indexer_kpool_shard_queries_across_tp: bool = False(CLI--dsa-indexer-kpool-shard-queries-across-tp). When it is set and TP > 1,DSAttentionpasses the TP group for the KPool top-k.DSAIndexer.forward_with_scorespasses it whenreturn_index_scores=False, which is the pathDSAIndexer.forwarduses.DSAttentionskips sharding when the attention query rows are already sequence-local per TP rank (sequence_parallel_query_is_local). In that caseq/weightsare already sliced to the local rows and differ across ranks.query_start=0,query_end=sq, and no collective.Communication per indexer call: one all-gather of
batch * ceil(sq/TP) * (index_topk / kpool)int32 per rank. At 512k tokens,index_topk=2048,kpool=4and TP=8, that is 128 MiB per rank.Alternatives considered
TP x budgetcandidates per row) and changes tie-breaking. Splitting by query needs only a concatenation and keeps results identical.kpoolx larger (and int64). Gathering the pool IDs and expanding after the gather moves the least data.Test plan
tests/unit_tests/transformer/experimental_attention_variant/test_kpool_query_shard.py(43 cases, CPU-runnable, uses CUDA if available). It simulates every rank of a group in one process: a fake group providessize()/rank(), andtorch.distributed.all_gather_into_tensoris replaced by one that returns the shards every simulated rank produced. Each simulated rank's output is compared bitwise to the unsharded call:sqin {5, 24, 37} (ragged last shard, and empty shards whensq < TP), batch 1/2, with and without a small_KPOOL_SCORE_CHUNK_BYTESso shard edges fall inside the chunk loop;cu_seqlens_kv+ varlen bounds), shard sizes 2/4;ceil(sq/TP)rows;query_shard_groupwithreturn_index_scores=Trueraises.tests/unit_tests/models/test_hybrid_moe_model.py: added the new field toGOLDEN_CONFIG. Not run locally because it needs the distributed GPU harness.Run locally (CPU only,
CUDA_VISIBLE_DEVICES=""):Formatting:
black --skip-magic-trailing-comma --skip-string-normalization(26.3.0),isort(5.13.2),ruff check(0.9.x) andpylint(3.2.6, 10.00/10) on the changed files.Not run here: the multi-GPU DSA unit tests and a real NCCL run of this exact port.
Validation (from SkyRL)
The same change, with the same contiguous query split and the same int32 all-gather, has been running in SkyRL on a vendored copy of the #7522 KPool code: NovaSky-AI/SkyRL#2422. The model is GLM-5.3-Flash, run on 64 B200 (8 nodes x 8) with TP8:
The 256k step times and the microbenchmark come from SkyRL's long-context benchmark runs. Those runs used SkyRL-specific settings, including a per-step
torch.cuda.empty_cache(), so the absolute step times will differ elsewhere. The speedup depends on how much of the step the indexer takes, which grows with sequence length. In SkyRL's 2-rank NCCL test (test_dsa_kpool_tp_shard.pyin #2422), sharded selections were bitwise identical to a single rank.Risks / notes for reviewers
dsa_indexer_kpool > 1) is covered. The per-token indexer (fused_qk_topk_naive/ fused kernels) is unchanged.Issue tracking
Linked issue:
Contribution process
Pre-checks
jit_fuser/torch.compile, CUDA extension, TE or external-library dispatch, or a scatter/index accumulation), I have added or updated its bit-exact determinism test and registered it intests/unit_tests/determinism/kernels/manifest.py(guide)🤖 Generated with Claude Code