vllm.models.deepseek_v41.nvidia.specialized_decode.attention.indexer
¶
The sparse-attention indexers in a specialized decode step.
decode_rows runs a dense indexer (SparseAttnIndexer) on a copy of the
varlen decode branch of sparse_attn_indexer, with two additions:
- Row sharding. The indexer is replicated across the TP ranks. In a uniform
decode step of at least
ROW_SHARD_MIN_ROWSrows, each rank scores, selects candidates for and top-k's only its contiguous slice of the rows (an even number of them, so the last ranks' slices may be short or empty); TP all-gathers of the int32 rows (see_all_gather_rows; the executor'sLamportAllGather) rebuildtopk_indices_bufferand, in the candidate source, the candidate blocks. A slice may split a request: varlen rows are independent. - Selection from the BF16 logits. For up to 64 rows per rank,
decode_topk(decode_topk_cute) selects the token top-k, and in the candidate source the candidate blocks, from SM100's BF16 logits without upcasting them. The candidate source's logits are twice as wide (compress ratio 1), so at a 1M context they need 16 CTAs a row and take that path only up to two waves of such clusters (14 rows on a GB300); otherwise the candidate source selects withcooperative_topk(seecandidate_topk) on the upcast logits.
Steps it does not take run the stock indexer.
decode_rows runs in two parts. plan_decode_rows takes the gates,
resets the top-k rows and builds the slice's DeepGEMM schedule: none of it
reads the layer's index query or keys, so the attention step issues it while
the current stream would otherwise wait for them. finish_decode_rows
scores, selects and gathers once they are ready.
CandidateOverlap moves the candidate consumers' candidate expansion and
sparse MQA-logits schedule onto a side stream.
The row-sharded path issues TP collectives, so its gates read only host state that every TP rank shares: the step's row and request counts.
Classes:
-
CandidateOverlap–The candidate consumers' expansion and schedule on a side stream.
-
DecodeRowsPlan–The rows a rank scores in
finish_decode_rowsand their schedule.
Functions:
-
decode_rows–op(hidden_states, q_quant, None, weights)for a pure-decode step, -
decode_schedule–Expand the decode rows' candidates into
rows.sparse_indices/ -
finish_decode_rows–The rest of
decode_rowsonce the index query and keys are ready: -
plan_decode_rows–The first part of
decode_rowsfor a step ofnum_tokenstokens: -
rope_quant_q–The second half of
indexer.forward_q: RoPE and quantize the -
row_shard_bounds–Rows
[start, end)thatrankscores in a pure-decode step (an -
row_shard_rows–Rows each rank's slice spans in a pure-decode step, or None when the
CandidateOverlap
¶
The candidate consumers' expansion and schedule on a side stream.
The first candidate consumer of a decode step expands the candidate blocks
into DeepGEMM sparse block ids and builds the paged sparse MQA-logits
schedule, which later consumers reuse. Neither reads Q, the KV cache or the
hidden states, so the candidate source forks them onto stream once it
has written the candidates (fork) and every consumer waits for them
(join) before scoring. The kernels and inputs are those of the inline
path, so the outputs match.
The side stream issues no collectives. The fork waits on the main stream,
so a step's writes to the builder's sparse_indices / end cannot
overtake the previous step's reads, and the schedule allocated on
stream is recorded as used by the main stream.
DeepGEMM's schedule kernel keeps a per-stream workspace that it creates on
first use and refuses to create during a capture, so warm runs the
schedule on stream once from an eager decode step, on zero candidates
and private outputs; the executor forks only after that.
Methods:
-
fork–Start the expansion; the candidate source calls this right after
-
join–Make the current stream wait for a pending fork, if any.
-
warm–Create the side stream's schedule workspace from an eager step.
Source code in vllm/models/deepseek_v41/nvidia/specialized_decode/attention/indexer.py
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 | |
fork()
¶
Start the expansion; the candidate source calls this right after its indexer has written the candidates.
Source code in vllm/models/deepseek_v41/nvidia/specialized_decode/attention/indexer.py
join()
¶
Make the current stream wait for a pending fork, if any.
warm()
¶
Create the side stream's schedule workspace from an eager step.
Source code in vllm/models/deepseek_v41/nvidia/specialized_decode/attention/indexer.py
DecodeRowsPlan
dataclass
¶
The rows a rank scores in finish_decode_rows and their schedule.
Source code in vllm/models/deepseek_v41/nvidia/specialized_decode/attention/indexer.py
_all_gather_rows(buffer, rows, num_rows, all_gather)
¶
Gather every rank's rows-row slice of buffer into its first
num_rows rows; rows past num_rows (a short or empty last slice)
are dropped. The slices go through all_gather when it takes them,
else NCCL.
Source code in vllm/models/deepseek_v41/nvidia/specialized_decode/attention/indexer.py
_decode_rows(op, q, q_scale, weights, plan)
¶
The varlen decode branch of sparse_attn_indexer on the planned rows
[start, end): paged MQA logits, candidate selection or mask, and
top-k into the same rows of the shared buffers.
Source code in vllm/models/deepseek_v41/nvidia/specialized_decode/attention/indexer.py
_selects_from_bf16(op, logits, fast_candidates)
¶
Whether decode_topk selects the token top-k, and the candidate
blocks if the layer writes them, from these logits.
Source code in vllm/models/deepseek_v41/nvidia/specialized_decode/attention/indexer.py
decode_rows(op, hidden_states, q_quant, weights, all_gather=None)
¶
op(hidden_states, q_quant, None, weights) for a pure-decode step,
row-sharded and with cooperative candidates where they apply; the row
slices are gathered with all_gather where it takes them.
Source code in vllm/models/deepseek_v41/nvidia/specialized_decode/attention/indexer.py
decode_schedule(op, rows, sparse_block_kv, scratch=False)
¶
Expand the decode rows' candidates into rows.sparse_indices /
rows.end and return their DeepGEMM schedule; the same kernels and
inputs as the inline step of sparse_mqa_logits_paged_decode. With
scratch, expand zero candidates into private outputs instead.
Source code in vllm/models/deepseek_v41/nvidia/specialized_decode/attention/indexer.py
finish_decode_rows(op, hidden_states, q_quant, weights, plan, all_gather=None)
¶
The rest of decode_rows once the index query and keys are ready:
score and select the planned rows and gather the slices (with
all_gather where it takes them).
Source code in vllm/models/deepseek_v41/nvidia/specialized_decode/attention/indexer.py
plan_decode_rows(op, num_tokens)
¶
The first part of decode_rows for a step of num_tokens tokens:
reset the top-k rows and build the slice's schedule. None when the stock
indexer runs instead (and nothing was issued).
Source code in vllm/models/deepseek_v41/nvidia/specialized_decode/attention/indexer.py
rope_quant_q(indexer, q, indexer_weights, positions, rotary_emb)
¶
The second half of indexer.forward_q: RoPE and quantize the
projected queries q and scale the per-head indexer_weights.
Source code in vllm/models/deepseek_v41/nvidia/specialized_decode/attention/indexer.py
row_shard_bounds(num_rows, num_requests, world_size, rank)
¶
Rows [start, end) that rank scores in a pure-decode step (an
empty range for a rank past the last row), or None when the step stays
replicated.
Source code in vllm/models/deepseek_v41/nvidia/specialized_decode/attention/indexer.py
row_shard_rows(num_rows, num_requests, world_size)
¶
Rows each rank's slice spans in a pure-decode step, or None when the
step stays replicated. Rank r scores rows [r n, (r + 1) n)
clipped to the step's rows.
Slices start on an even row: DeepGEMM's paged MQA logits needs its int32
indices 8-byte aligned. Varlen rows are independent, so a slice may
split a request. Steps whose rows are not a multiple of their requests
(some capture and profiling runs) stay replicated.