[CUDA] MatMulBlockQuantizedFp8Weight: fold the W8A8 activation QDQ into the decode GEMV - #31481
Open
tianleiwu wants to merge 2 commits into
Open
[CUDA] MatMulBlockQuantizedFp8Weight: fold the W8A8 activation QDQ into the decode GEMV#31481tianleiwu wants to merge 2 commits into
tianleiwu wants to merge 2 commits into
Conversation
…to the GEMV
With an activation scale the operator ran QuantizeDequantizeActivationFp8Kernel over
A into a scratch buffer and then read that buffer back in the GEMV. At decode shapes
(m <= 8) A is tiny, so the round trip is almost entirely launch and memory-round-trip
overhead.
Apply the same quantize-dequantize in registers on the activations the GEMV has
already loaded. Fp8ActQdq16 mirrors the standalone kernel element for element, so the
result is bit-identical -- verified over 26112 elements across six shapes covering
m in {1,2,4}, block_size in {16,128,2048} and both the MMA and scalar GEMV kernels.
Only the m <= 8 GEMV path is affected; the cuBLAS path still uses the standalone
kernel, so its [M, K] scratch buffer is now only allocated for that path.
Measured on Qwen3.6-35B-A3B MTP N=3 (H200): the kernel disappears from the trace,
-40 launches/step and -54.3 us/step. In isolation the win is largest at small N
(-9.7% at N=512) and shrinks as N grows, because the fused kernel redoes the QDQ once
per A-load.
Opt out with ORT_DISABLE_FUSED_FP8_ACT_QDQ=1.
…tenv
MSVC treats C4996 ('getenv' is unsafe) as an error in the Windows GPU CI
builds, breaking the CUDA, CUDA plugin EP, TensorRT and kernel-doc jobs.
Use ORT's cross-platform env var helper, matching other CUDA kernels.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR optimizes the CUDA contrib op MatMulBlockQuantizedFp8Weight decode GEMV fast path (small M) by fusing the optional W8A8 activation quantize/dequantize (QDQ) into the GEMV kernels, avoiding a separate QDQ kernel launch and an [M, K] scratch-buffer round trip.
Changes:
- Add an optional
act_scaleparameter to the FP8 decode GEMV launcher and kernels, enabling in-register W8A8 activation QDQ during GEMV. - Gate allocation/use of the activation scratch buffer so it’s only created for the cuBLAS GEMM path (or when fused QDQ is explicitly disabled via
ORT_DISABLE_FUSED_FP8_ACT_QDQ=1). - Add an environment-variable kill switch to disable the fused activation QDQ.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| onnxruntime/contrib_ops/cuda/math/matmul_block_scaled_fp8.h | Extends GEMV launcher API with optional act_scale and documents fused activation QDQ behavior. |
| onnxruntime/contrib_ops/cuda/math/matmul_block_scaled_fp8.cu | Implements the in-register activation QDQ helper and wires act_scale through scalar + MMA GEMV kernels. |
| onnxruntime/contrib_ops/cuda/math/matmul_block_scaled_fp8.cc | Adds kill-switch env var and avoids allocating activation scratch when fused QDQ is enabled for decode GEMV. |
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.
Description
When
MatMulBlockQuantizedFp8Weightis given an activation scale (the W8A8 path), it ranQuantizeDequantizeActivationFp8Kernelover the activationAinto a scratch buffer, then read that buffer back in the GEMV. At decode shapes (m <= 8)Ais tiny, so that round trip is almost entirely launch overhead and a pointless trip to memory.This applies the same quantize-dequantize in registers, on the activation values the GEMV has already loaded.
Fp8ActQdq16mirrors the standalone kernel element for element, so the result is bit-identical.Scope is limited to the
m <= 8GEMV fast path. The cuBLAS path is unchanged and still uses the standalone kernel, so the[M, K]scratch buffer is now only allocated when that path is actually taken.Opt out with
ORT_DISABLE_FUSED_FP8_ACT_QDQ=1.Verification
Bit-identical to the previous two-step path, checked over 26112 elements across six shapes covering
min {1, 2, 4},block_sizein {16, 128, 2048}, and both the MMA and scalar GEMV kernels.Motivation and Context
Measured on Qwen3.6-35B-A3B MTP (N=3) on H200: the standalone QDQ kernel disappears from the trace entirely, removing 40 launches/step and 54.3 us/step.
In isolation the win is largest at small N (-9.7% at N=512) and shrinks as N grows, because the fused kernel redoes the QDQ once per A-load rather than once per element. That trade is favorable exactly in the decode regime this fast path serves.
Builds on the decode GEMV added in #31155.