Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions tensorrt_llm/_torch/attention_backend/sparse/dsa.py
Original file line number Diff line number Diff line change
Expand Up @@ -2575,8 +2575,19 @@ def sparse_attn_indexer(
num_k_tokens, gather_head_dim)

chunk_num_token = chunk.token_end - chunk.token_start
apply_q_split = q_split_eligible and chunk_num_token >= q_split_threshold
if apply_q_split:
# Always q-split when eligible: redundant per-rank
# `fp8_mqa_logits` / `fp8_fp4_mqa_logits` are not bit-exact
# across launches, so different TP ranks produce different
# topk indices for the same tokens. The downstream MLA
# attention then attends to divergent KV positions on
# different ranks, corrupting KV-cache writes -- invisible
# for short generations (e.g. MMLU's 2-token answers) but
# accumulates into garbage over long ones (GSM8K's 256
# tokens) -> 0% accuracy. The q-split + allgather path
# canonicalizes ownership per-token, so any rank-local
# nondeterminism is erased before downstream layers read
# the indices. q_split_threshold < 0 still fully disables.
if q_split_eligible:
chunk_q_start = chunk_num_token * tp_rank // tp_size
chunk_q_end = chunk_num_token * (tp_rank + 1) // tp_size
else:
Expand Down Expand Up @@ -2643,7 +2654,7 @@ def sparse_attn_indexer(
g0:g1, :topk_indices.shape[-1]] = \
topk_indices.to(dtype=torch.int32)

if apply_q_split:
if q_split_eligible:
q_sizes = [(r + 1) * chunk_num_token // tp_size -
r * chunk_num_token // tp_size
for r in range(tp_size)]
Expand Down
Loading