vllm.model_executor.kernels.linear.mxfp8.rocm_block32_gemm
¶
MXFP8 GEMM on 32x32 block-scaled weights for gfx950 (tl.dot_scaled).
y = x @ w.T with an MXFP8 activation (e4m3 values, one E8M0 scale per
row and 32 K, [M, K / 32]) and an e4m3 weight whose E8M0 scales stay in
the checkpoint's 32x32 blocks, [N / 32, K / 32], instead of being
expanded to every row. Each weight scale byte is then read once per 32 output
rows, and small-M shapes can use the packed kernel below.
Two kernels, picked per shape from a table tuned on MI355X:
- a tiled kernel for larger M;
- a packed kernel for small M, which fills the MFMA's rows with K panels instead of tokens and keeps only the matching-panel (block-diagonal) products, so a handful of tokens still streams the weight at full width.
Either can split K. The partials are normally summed in the same launch by the last program of each output tile to finish, in split order, so the result does not depend on scheduling. Otherwise a second launch reduces them.
The packed kernel and the in-launch split-K reduction on one XCD
(_split_tile, _sum_splits, _split_counters, _k_partition) are
adapted from the group32 GEMM in ROCm/aiter#5750.
Functions:
-
rocm_mxfp8_block32_gemm–x @ weight.Tfor MXFP8xand a 32x32 block-scaled MXFP8 weight. -
rocm_mxfp8_block32_gemm_quant–x @ weight.Tfor a raw bf16/fp16x[M, K] and a 32x32
_default_config(M, N, K)
¶
Untuned shapes: the tiers most tuned shapes settle on.
Source code in vllm/model_executor/kernels/linear/mxfp8/rocm_block32_gemm.py
_fused_quant_config(M, N, K)
¶
The stock tier active at M with its tiling/scheduling retuned for the in-kernel quant prologue (see _TUNED_FUSED_QUANT); the stock config where the shape has no fused-tier entry. K-structure axes are always the stock tier's own, so the output stays bit-exact at every M.
Source code in vllm/model_executor/kernels/linear/mxfp8/rocm_block32_gemm.py
_k_partition(K, splits, step)
¶
K per split, rounded up to whole steps, and the number of non-empty splits.
Source code in vllm/model_executor/kernels/linear/mxfp8/rocm_block32_gemm.py
_launch(x, x_scale, weight, weight_scale, out, cfg, quant_in_kernel=False)
¶
Launch cfg's kernel. quant_in_kernel=True (packed cfgs only) makes x the raw bf16/fp16 activation and x_scale None: the kernel quantizes x per 32-block in registers (see _block32_packed_quant_kernel).
Source code in vllm/model_executor/kernels/linear/mxfp8/rocm_block32_gemm.py
762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 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 | |
_rocm_mxfp8_block32_gemm_quant_impl(x, weight, weight_scale, out_dtype)
¶
x @ weight.T for raw bf16/fp16 x and a 32x32 block-scaled
MXFP8 weight, quantizing x inside the GEMM at tiny M.
M <= 8 with a packed tile config (every tuned M <= 8 tier is packed)
runs one launch whose K loop computes the per-32-block E8M0 scale and
the e4m3 cast exactly as _mxfp8_quant_triton_kernel and feeds
tl.dot_scaled from registers -- bit-identical output to
mxfp8_e4m3_quantize + rocm_mxfp8_block32_gemm with one launch
fewer on the decode critical path. Everything else (larger M,
non-packed tiles, or non-contiguous or non-bf16/fp16 x) runs the stock
two-launch path with identical results. The branch is inside this
opaque op so a compiled caller cannot freeze the token-count decision.
Source code in vllm/model_executor/kernels/linear/mxfp8/rocm_block32_gemm.py
_split_counters(device)
¶
Zeroed arrival counters, one set per stream so concurrent launches never share a tile's counter. Each launch leaves the counters it used at zero.
None when a stream first needs them inside a CUDA graph capture: that allocation would come from the graph's pool, where it can take the address of an intermediate that every replay overwrites. vLLM warms up on its capture streams first, so this only affects captures without a warmup.
Source code in vllm/model_executor/kernels/linear/mxfp8/rocm_block32_gemm.py
_split_tile(N, BLOCK_N, SPLITS)
¶
(pid_m, pid_n, tile, split) of a 1D grid that keeps a tile's splits on one XCD, so they meet in that XCD's L2.
Source code in vllm/model_executor/kernels/linear/mxfp8/rocm_block32_gemm.py
_sum_splits(acc, out_ptrs, out_mask, slot_ptr, count_ptr, split, SPLITS)
¶
Publish this split's partial; the tile's last arrival sums all of them in split order and re-arms the counter.
Source code in vllm/model_executor/kernels/linear/mxfp8/rocm_block32_gemm.py
rocm_mxfp8_block32_gemm(x, x_scale, weight, weight_scale, out_dtype)
¶
x @ weight.T for MXFP8 x and a 32x32 block-scaled MXFP8 weight.
Parameters:
-
(x¶Tensor) –[M, K] e4m3 activation, contiguous.
-
(x_scale¶Tensor) –[M, K / 32] E8M0 (uint8) activation scales.
-
(weight¶Tensor) –[N, K] e4m3 weight, contiguous.
-
(weight_scale¶Tensor) –[ceil(N / 32), K / 32] E8M0 (uint8) weight block scales.
-
(out_dtype¶dtype) –Output dtype.
Returns:
-
Tensor–The [M, N] product.
Source code in vllm/model_executor/kernels/linear/mxfp8/rocm_block32_gemm.py
rocm_mxfp8_block32_gemm_quant(x, weight, weight_scale, out_dtype)
¶
x @ weight.T for a raw bf16/fp16 x [M, K] and a 32x32
block-scaled MXFP8 weight [N, K] with weight_scale
[N / 32, K / 32]. Tiny-M batches quantize inside the packed GEMM (see
_rocm_mxfp8_block32_gemm_quant_impl).