Repository navigation
[ROCm][Quantization] Fuse the block Hadamard input rotation into AITER's MXFP4 activation quant - #63272
Draft
mjkvaak-amd wants to merge 3 commits into
Draft
[ROCm][Quantization] Fuse the block Hadamard input rotation into AITER's MXFP4 activation quant#63272mjkvaak-amd wants to merge 3 commits into
mjkvaak-amd wants to merge 3 commits into
Conversation
Add InputHadamardLinearMethod, which wraps any linear method and rotates the layer input by an orthonormal block Hadamard matrix before the wrapped apply, in the manner of CompressedTensorsLinearTransformMethod. The rotation is independent of the weight format and backend. Two producers use it: - online quantization: QuantSpec.hadamard_size and the mxfp4_hadamard32 shorthand (also per layer via --quantization-config.targets); the weight is rotated once before the online method quantizes it. - Quark MXFP4 checkpoints exported with an online rotation: weights are stored rotated and the int8 input_rotation tensor is checked to be the Sylvester matrix, then dropped. Paths that have not been validated (online methods other than MXFP4 linear, MoE, requantization, Quark formats other than MXFP4, non-Sylvester rotations) raise NotImplementedError. Signed-off-by: Mikko Tukiainen <Mikko.Tukiainen@amd.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Contributor
|
Documentation preview: https://vllm--63272.org.readthedocs.build/en/63272/ |
1 task done
6 tasks done
…reload - InputHadamardLinearMethod registers itself with register_weight_loader_v2_supported_method instead of being listed in WEIGHT_LOADER_V2_SUPPORTED. - QuantSpec validates that hadamard_size is a power of two; drop the duplicate checks in the online config and the wrapper's constructor (hadamard_matrix keeps its own). - Test layerwise weight reloading for both the online and the Quark checkpoint path; use set_random_seed in the tests. Co-authored-by: Cursor <cursoragent@cursor.com> Signed-off-by: Mikko Tukiainen <Mikko.Tukiainen@amd.com>
…R's MXFP4 quant Add LinearMethodBase.fuse_input_hadamard(layer, size), default False. InputHadamardLinearMethod calls it after the wrapped method's process_weights_after_loading and skips its own input rotation when the method takes it over. Online MXFP4 and Quark OCP MX forward the call to their MxFp4LinearKernel; AiterMxfp4LinearKernel accepts on the ASM GEMM path at size 32 and passes hadamard_rotate=True to the HIP activation quant (ROCm/aiter#6734). hadamard_rotate is only passed to AITER when set, so older AITER keeps working as long as nothing fuses. Also fix the fake implementation of gemm_with_dynamic_quant, whose signature had drifted from the op. Co-authored-by: Cursor <cursoragent@cursor.com> Signed-off-by: Mikko Tukiainen <Mikko.Tukiainen@amd.com>
6 tasks done
mjkvaak-amd
force-pushed
the
rocm/mxfp4-hadamard32-aiter-fused
branch
from
October 23, 2026 06:00
2b7f9ef to
93cc714
Compare
1 task done
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overview
Stacked on #62750: only the last commit belongs to this PR. Draft until ROCm/aiter#6734 is in an AITER release that vLLM pins.
#62750 rotates the input of
mxfp4_hadamard32layers in PyTorch (InputHadamardLinearMethod) before the wrapped linear method quantizes it. On AITER's MXFP4 path this adds a separate rotation kernel and a round trip of the activations through memory, which roughly doubles the per-layer time of small GEMMs (see Test Result).ROCm/aiter#6734 adds
hadamard_rotate=Truetoper_1x32_f4_quant_hip, which rotates each 32-wide block inside the activation quant. This PR uses it:LinearMethodBase.fuse_input_hadamard(layer, size) -> bool, defaultFalse.InputHadamardLinearMethodcalls it after the wrapped method'sprocess_weights_after_loading; if the method returnsTrue, the wrapper keeps rotating the weight but skips its own input rotation. This is the generic hook promised in [Quantization] Block Hadamard input rotation for linear layers: onlinemxfp4_hadamard32and Quark online-rotation checkpoints #62750: any backend whose activation quant can rotate can take over, and nothing else changes.Mxfp4OnlineLinearMethod) and Quark OCP MX (QuarkOCP_MXthroughQuarkLinearMethod/QuarkScheme) forward the call to theirMxFp4LinearKernel.AiterMxfp4LinearKernelaccepts when it uses the ASM GEMM and the size is 32, and passeshadamard_rotate=Truetogemm_with_dynamic_quant, which forwards it to both HIP quant calls. The Triton branch quantizes withdynamic_mxfp4_quant, which has no rotation, so that path keeps the PyTorch rotation (asserted in the op).hadamard_rotateis only passed to AITER when it isTrue, so AITER releases without [Bug]: vllm-0.5.3.post1部署Qwen2-72b-instruct-awq模型,刚开始服务正常,但是并发高的时候就报错 #6734 keep working as long as nothing fuses.gemm_with_dynamic_quant, whose signature had drifted from the op (missingx_scales, wrong argument order), and adds aFakeTensorModetest for it.The server logs once per method type:
Mxfp4OnlineLinearMethod rotates its input by the 32-wide Hadamard matrix itself.When the ASM path is used
AiterMxfp4LinearKerneluses the ASM GEMM only for (N, K) present in AITER's tuned a4w4 config (is_asm_fp4_gemm_tuned, M = 1024 and 8192). On Qwen3.8-2.4T at TP8,out_proj(N = 8192, K = 2048) is tuned in AITER today, butin_proj_qkvz(N = 4608, K = 8192) is not, so it runs the Triton GEMM and can't fuse. A tuned config for that shape (26 values of M, all won by ASM kernels, error ratio 0) movesin_proj_qkvzto ASM; it is part of ROCm/aiter#6734. The numbers below include it in every arm.Test Plan
tests/quantization/test_input_hadamard.py(fused flag on the ASM shape and not on the Triton or emulation paths; rotated MXFP4 error below plain; the wrapper doesn't rotate twice when a method fuses),tests/quantization/test_online_mxfp4.py(fake op dtype and shape),tests/quantization/test_quantization_config_args.py,tests/v1/determinism/test_mx_linear_batch_invariant.py.--load-format dummy),vllm bench serverandom ISL/OSL 1024/1024 at concurrency 4, 64 and 256, 2 reps per arm, fresh caches per arm: RTNmxfp4,mxfp4_hadamard32with the PyTorch rotation ([Quantization] Block Hadamard input rotation for linear layers: onlinemxfp4_hadamard32and Quark online-rotation checkpoints #62750), andmxfp4_hadamard32fused (this PR) on the GDN projections. Shared experts are onlinemxfp4in every arm. Images: vLLM nightly df3bd09 plus the respective diff, [HIP] Fuse a 32-point Hadamard rotation into the MXFP4 per-1x32 quant and AR + RMSNorm epilogue ROCm/aiter#6734 ported to its AITER 0.1.25, and the tuned config.Test Result
Per layer, µs per call (quant + GEMM, CUDA graph, MI355X, AITER a4w4 tuning for
in_proj_qkvzapplied):in_proj_qkvzplainout_projplainThe PyTorch rotation adds 70–170%; the fused rotation adds 0–5%. Compiling the PyTorch rotation (
torch.compile) doesn't help. Without the tuned config,in_proj_qkvzruns the Triton GEMM (10.8 µs at M = 1, 120 µs at M = 1024) and keeps the PyTorch rotation.Fused vs PyTorch rotation: same relative error against the unquantized GEMM to five digits at every M. Up to 0.07% of output elements differ, because the fused quant rotates with an fp32 butterfly while the PyTorch path uses a bf16 matmul with a different summation order. Both round the rotated activations to bf16, and the rare values that round differently can land on the other side of an FP4 rounding boundary. Both paths are deterministic run to run.
TP8 serving (Qwen3.8-2.4T-A95B Quark MXFP4, dummy weights, ISL/OSL 1024/1024, mean of 2 reps):
mxfp4mxfp4_hadamard32, PyTorch rotation (#62750)mxfp4_hadamard32, fused (this PR)Repeats differ by up to 1.6%. The server log of the fused arm has the "rotates its input" line; the other two don't. All three arms quantize the same 322 layers.
The rotation is the same as in #62750, so the accuracy results there carry over; the fused and unfused outputs differ only as described above.
Submission Checklist
mxfp4_hadamard32and Quark online-rotation checkpoints #62750 once it merges; bump AITER after [HIP] Fuse a 32-point Hadamard rotation into the MXFP4 per-1x32 quant and AR + RMSNorm epilogue ROCm/aiter#6734 is released.