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 step of at
least
ROW_SHARD_MIN_REQUESTSrequests whose count the ranks divide, each rank scores, selects candidates for and top-k's only its contiguous slice of the rows; TP all-gathers of the int32 rows rebuildtopk_indices_bufferand, in the candidate source, the candidate blocks. - The candidate source selects its candidate blocks with
cooperative_topkfor up to 64 rows per rank (seecandidate_topk).
Steps it does not take run the stock indexer.
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.
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/ -
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, or
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
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 | |
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
_decode_rows(op, q, q_scale, weights, decode, max_seq_len, start, end, fast_candidates)
¶
The varlen decode branch of sparse_attn_indexer on 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
decode_rows(op, hidden_states, q_quant, weights)
¶
op(hidden_states, q_quant, None, weights) for a pure-decode step,
row-sharded and with cooperative candidates where they apply.
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
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, or
None when the step stays replicated.
Each slice holds whole requests of a uniform step; varlen rows are
independent, so the split is exact either way. Slices start on an even
row: DeepGEMM's paged MQA logits needs its int32 indices 8-byte
aligned. DSpark decode steps always split so; capture and profiling runs
of one row a request may not, and stay replicated.