vllm.v1.attention.ops.rocm_aiter_mla_sparse ¶
Functions:
-
fp8_mqa_logits_torch–Compute FP8 MQA logits for a single sequence without KV paging.
-
rocm_fp8_mqa_logits–Compute FP8 MQA logits for a single sequence without KV paging.
-
rocm_fp8_paged_mqa_logits–Compute FP8 MQA logits using paged KV-cache.
-
rocm_fp8_paged_mqa_logits_triton–Triton paged MQA-logits for decode and MTP; matches the torch ref but
-
rocm_inv_rope_einsum–Inverse-RoPE + WO_A bmm path used on ROCm.
_apply_candidate_mask_strided(logits, row_ks, row_ke, candidate_blocks, block_size, row_repeat=1) ¶
ROCm decode variant of apply_candidate_mask.
Same masking semantics over [0, end), but the grid is sized by a fixed program count rather than by the logits width. Only worth using where the width is the max_model_len workspace and the live context is far shorter, i.e. the paged decode path below; the prefill chunks pass chunk-sized logits and stay on the shared kernel.
Source code in vllm/v1/attention/ops/rocm_aiter_mla_sparse.py
_decode_num_splits(num_queries, heads_blocks, avg_main_len=0.0, avg_extra_len=0.0, block_k=32) ¶
Pick a flash-decode split count to keep the GPU busy across batch sizes.
Decode launches only num_queries * heads_blocks workgroups otherwise, which severely under-fills the device for the low-concurrency regime that dominates latency. Splitting the KV sequence adds parallelism.
We model the relative partial-kernel latency for a given split count s as waves * (1/s + mu) where waves = ceil(base * s / CU) and mu is a small per-wave overhead penalty:
waves / scaptures the partial compute: each wave walks roughlytotal_tokens / stokens and there arewavesof them, so dividing bysmakes more splits cheaper until they spill into extra waves.mu * wavescharges per-wave launch/tail overhead so we do not over-split into many mostly-idle waves (e.g. batch 224 on 256 CUs is best left at 1 split rather than 8 splits across 7 waves).
The minimiser naturally prefers split counts that pack the device into full waves (base * s near a multiple of CU) and falls back to 1 split once the batch already fills the device. Ties favour the smaller split count (less reduce work).
Finally we "snap down" the chosen split count to the smallest value that yields the same wave count and the same per-workgroup BLOCK_K iteration count. Because latency tracks iteration count (not raw token count), extra splits that do not lower the iteration count add only reduce/HBM overhead for no parallelism gain (e.g. batch 24: s8 and s10 both walk 4 extra iters in one wave, so s8 is strictly better). Snapping needs the average segment lengths, which the caller derives sync-free from the ragged index sizes.
Source code in vllm/v1/attention/ops/rocm_aiter_mla_sparse.py
3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 | |
_decode_partial_iters(avg_main_len, avg_extra_len, splits, block_k) ¶
BLOCK_K iterations one partial workgroup walks for splits splits.
Each split processes ceil(seg_len / splits) tokens of a segment, walked BLOCK_K at a time, and the main/extra segments are handled separately.
Source code in vllm/v1/attention/ops/rocm_aiter_mla_sparse.py
_fused_inverse_rope_gptj(o, positions, cos_sin_cache, rope_head_dim) ¶
bf16 inverse GPT-J RoPE via a single fused Triton kernel.
Source code in vllm/v1/attention/ops/rocm_aiter_mla_sparse.py
_get_cached_wo_a_bf16(wo_a, n_local_groups, o_lora_rank, hidden_dim) ¶
Dequantize wo_a to bf16 once and cache it on the module.
wo_a weights are static, so the fp8 -> fp32 -> (* block scale) -> bf16 dequant only needs to run once. Recomputing it every decode step shows up in the profile as the largest copy/mul kernels (direct_copy float ~55us and MulFunctor float ~31us per two layers). SGLang / ATOM keep wo_a in bf16 and feed a plain bf16 GEMM; this mirrors that.
Source code in vllm/v1/attention/ops/rocm_aiter_mla_sparse.py
_indexer_k_is_c4a_block_flat(compress_ratio) ¶
_inverse_rope_gptj_kernel(o_ptr, out_ptr, pos_ptr, cos_sin_ptr, s_t, s_h, os_t, os_h, cs_stride, NOPE, HALF, BLOCK_NOPE, BLOCK_HALF) ¶
Fused inverse GPT-J RoPE on the trailing rope_dim of each (token, head).
Mirrors DeepseekV4ScalingRotaryEmbedding.forward_native(inverse=True) for the GPT-J (non-neox) layout, writing bf16 directly. Replaces the clone + index_select + repeat_interleave + neg + stack + cat + cast chain (~10 small kernels) with a single launch.
Source code in vllm/v1/attention/ops/rocm_aiter_mla_sparse.py
_max_decode_logits_rows(num_batched_tokens) ¶
Upper bound on decode rows the paged-MQA logits buffer can ever hold.
rocm_fp8_paged_mqa_logits sizes its workspace as (batch_size * next_n, max_model_len). batch_size is bounded by max_num_seqs and next_n by 1 + num_speculative_tokens, which is far tighter than max_num_batched_tokens -- 192 vs 16384 for a typical 32-seq DSpark-5 deployment. The loose bound is harmless at short contexts but scales with max_model_len, so at the model's full context it asks for tens of TiB and the engine cannot start. Take whichever valid bound is smaller; the workspace is locked after profiling, so it must not be under- estimated.
Source code in vllm/v1/attention/ops/rocm_aiter_mla_sparse.py
fp8_mqa_logits_torch(q, kv, weights, cu_seqlen_ks, cu_seqlen_ke) ¶
Compute FP8 MQA logits for a single sequence without KV paging.
Parameters:
-
(q¶Tensor) –Query tensor of shape [M, H, D]. Casted to
torch.float8_e4m3fnby caller. -
(kv¶tuple[Tensor, Tensor]) –Tuple
(k_fp8, k_scales)wherek_fp8has shape [N, D] with dtypetorch.float8_e4m3fnandk_scaleshas shape [N] (or [N, 1]) with dtypetorch.float32. -
(weights¶Tensor) –weights of shape [M, H], dtype
torch.float32. -
(cu_seqlen_ks¶Tensor) –Start indices (inclusive) for valid K per query position, shape [M], dtype int32.
-
(cu_seqlen_ke¶Tensor) –End indices (exclusive) for valid K per query position, shape [M], dtype int32.
Returns:
-
Tensor–Logits tensor of shape [M, N], dtype
torch.float32.
Source code in vllm/v1/attention/ops/rocm_aiter_mla_sparse.py
rocm_fp8_mqa_logits(q, kv, weights, cu_seqlen_ks, cu_seqlen_ke) ¶
Compute FP8 MQA logits for a single sequence without KV paging.
Parameters:
-
(q¶Tensor) –Query tensor of shape [M, H, D]. Casted to
torch.float8_e4m3fnby caller. -
(kv¶tuple[Tensor, Tensor]) –Tuple
(k_fp8, k_scales)wherek_fp8has shape [N, D] with dtypetorch.float8_e4m3fnandk_scaleshas shape [N] (or [N, 1]) with dtypetorch.float32. -
(weights¶Tensor) –weights of shape [M, H], dtype
torch.float32. -
(cu_seqlen_ks¶Tensor) –Start indices (inclusive) for valid K per query position, shape [M], dtype int32.
-
(cu_seqlen_ke¶Tensor) –End indices (exclusive) for valid K per query position, shape [M], dtype int32.
Returns:
-
Tensor–Logits tensor of shape [M, N], dtype
torch.float32.
Source code in vllm/v1/attention/ops/rocm_aiter_mla_sparse.py
rocm_fp8_paged_mqa_logits(q_fp8, kv_cache_fp8, weights, context_lens, block_tables, schedule_metadata, max_model_len, *, compress_ratio=1) ¶
Compute FP8 MQA logits using paged KV-cache.
Parameters:
-
(q_fp8¶Tensor) –Query tensor of shape [B, next_n, H, D]. Casted to
torch.float8_e4m3fnby caller. -
(kv_cache_fp8¶Tensor) –Paged KV-cache in packed FP8+scale layout with shape [num_blocks, block_size, 1, D+4], dtype
torch.uint8. -
(weights¶Tensor) –Tensor of shape [B * next_n, H], dtype
torch.float32. -
(context_lens¶Tensor) –Tensor of shape [B], dtype int32; effective context length for each batch element.
-
(block_tables¶Tensor) –Tensor of shape [B, max_blocks], dtype int32; maps logical block indices to physical blocks in the paged cache.
-
(schedule_metadata¶Tensor) –Returned by
get_paged_mqa_logits_metadata; used to distribute work across SMs. -
(max_model_len¶int) –Maximum sequence length used to size the logits output.
-
(compress_ratio¶int, default:1) –C4A (4) takes block-flat Triton; 1 and 2 stay on AITER.
Returns:
Source code in vllm/v1/attention/ops/rocm_aiter_mla_sparse.py
788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 | |
rocm_fp8_paged_mqa_logits_triton(q_fp8, kv_cache_fp8, weights, context_lens, block_tables, max_model_len) ¶
Triton paged MQA-logits for decode and MTP; matches the torch ref but has no host sync, so it is safe to capture under a full CUDA graph.
Source code in vllm/v1/attention/ops/rocm_aiter_mla_sparse.py
rocm_inv_rope_einsum(rotary_emb, o, positions, rope_head_dim, n_local_groups, o_lora_rank, wo_a) ¶
Inverse-RoPE + WO_A bmm path used on ROCm.
Fuses the inverse GPT-J RoPE into one Triton kernel and caches the bf16 wo_a weight so the per-step dequant disappears.