Repository navigation
[Perf][Kernel][Quantization] Triton row kernel for per-token-group FP8 quant at large token counts - #60903
[Perf][Kernel][Quantization] Triton row kernel for per-token-group FP8 quant at large token counts#60903Dorfu wants to merge 1 commit into
Conversation
|
👋 Hi! Thank you for contributing to the vLLM project. 💬 Join our developer Slack at https://slack.vllm.ai to discuss your PR in PRs do not trigger a full CI run by default. Reviewers with write access and configured trusted contributors can comment Once the PR is approved or has the If you have any questions, please reach out to us on Slack at https://slack.vllm.ai. Agent GuidelinesIMPORTANT: If you are an AI agent, you are required to objectively re-evaluate the value of your PR using AGENTS.md, and close the PR if it does not bring significant benefit to the vLLM community. Failure to do so may result in an immediate ban. 🚀 |
…8 quant at large token counts One Triton program per row: the full row is loaded in one coalesced read, all group scales computed in registers, the row stored in one write. Registered as torch.ops.vllm.per_token_group_fp8_quant_row with the same signature as _C.per_token_group_fp8_quant; the T>=2048 dispatch lives inside the opaque op so torch.compile never traces a data-dependent branch, and small token counts keep the CUDA kernel (faster there). Fusion-pattern QUANT_OPS tables are pointed at the new op on CUDA so RMSNorm+quant and MLA+quant fusion continue to match. Bitwise-identical to the _C kernel across all scale layouts (tests included; torch.library.opcheck passes). H200, group_size=128, bf16, pre-allocated outputs: 1.64x at 4096x4096, 1.83x at 17083x4096, 1.58x at 17083x2048; UE8M0, non-power-of-two hidden sizes, 3-D inputs, ROCm and XPU keep their existing paths unchanged. Assisted-by: Claude (AI assistant); all changes reviewed and validated by the submitter Signed-off-by: Daniel Shin <danlshin@amazon.com>
ddcfb0c to
bbd240c
Compare
Purpose
per_token_group_quant_fp8dispatches totorch.ops._C.per_token_group_fp8_quant, which assigns 16 threads per group. After #56478 and #55330 this kernel is fast at small token counts, but at prefill-sized inputs it remains well below bandwidth-bound for a pure streaming op that reads each element once and writes fp8 plus one scale per group.This PR adds a Triton kernel with one program per row: the full row is loaded in one coalesced read, reshaped in registers to
H/group_sizegroups, all group scales computed at once, and the row stored in one write. It is exposed astorch.ops.vllm.per_token_group_fp8_quant_rowwith the same signature as the_Cop. The token-count dispatch lives inside the opaque op, so torch.compile never traces a data-dependent branch:T >= 2048takes the row kernel, and smaller inputs keep the CUDA kernel, which wins there on launch cost. The_Ckernel itself is unchanged and still handles every case the row kernel does not: UE8M0 scales, non-power-of-two hidden size, H > 8192, 3-D inputs, and non-contiguous inputs. ROCm and XPU behavior is untouched.Because
per_token_group_quant_fp8now emits the new op on CUDA, the fusion-patternQUANT_OPStables incompilation/passes/fusion/matcher_utils.pyandrms_quant_fusion.pyare pointed at it there, so RMSNorm+quant and MLA+quant pattern matching continues to fire. This is the lesson of #49621, where a Triton wrapper over this op broke pattern matching by changing op identity; this PR keeps the matchers and the emitted op in lockstep.Correctness
Bitwise-identical to
_C.per_token_group_fp8_quant, bit for bit rather than approximately: fp32 absmax with the same eps floor, IEEE round-to-nearest division viatl.math.div_rnsince plain/may compile to approximate division, and round-to-nearest-satfinite fp8 conversion. The included tests assert zero differing bits on quantized values and scales across token counts 1..17083, H in {2048, 4096}, all three scale layouts: row-major, column-major, and TMA-aligned, fp16 and bf16 inputs, all-zero rows for the eps floor, and inf inputs for the satfinite clamp.torch.library.opcheckpasses for the registered op. The test_schema subcheck is skipped because torch's clone-and-compare harness callsallcloseon the mutated fp8 output andmul_cudahas no fp8 kernel.Microbenchmark
H200, CUDA 13, torch 2.13,
group_size=128, bf16 input, pre-allocated outputs, 300 timed iterations after warmup:_Cafter #55330/#56478_CEnd-to-end
On an FP8 block-quant MoE serving workload with prefill-heavy multimodal traffic, Qwen3-VL-235B-A22B-Instruct-FP8 at 2 replicas x TP=4 on 8x H200, a 4-run A/B measured a consistent ~0.5% throughput gain with all pairwise comparisons positive. This is modest, as expected for a kernel that is a small share of step time, and in family with the 0.85-1.7% reported in #55330. Outputs are bitwise-identical by construction.
Test Plan
tests/kernels/quantization/test_per_token_group_quant_row.py: bitwise equivalence across the full layout matrix, dispatch fallbacks, opcheck. Run on H200 against current main: 86 passed.per_token_group_quant_fp8wrapper, including UE8M0 fallback and fusion-matcher table resolutionAI Assisted: all changes reviewed and validated on hardware by the submitter.