Repository navigation
[CUDA] Add native dynamic W8A8 execution to block-FP8 QMoE - #33644
Open
Justin Chu (justinchuby) wants to merge 2 commits into
Open
Justin Chu (justinchuby) wants to merge 2 commits into
Justin Chu (justinchuby) wants to merge 2 commits into
Conversation
Add opt-in dynamic_fp8 activation quantization with native E4M3 MMA, FP32 block-scale accumulation and routed down-projection reduction. Preserve weight-only defaults and reject unsupported profiles explicitly. Cover numerical rounding boundaries, sparse layouts, CUDA graphs and full expert dimensions. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Signed-off-by: Justin Chu <justinchuby@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
The quantizer has unresolved launch-dimension and NaN-preservation defects.
2 open issues
What changed in this PR
Adds opt-in dynamic FP8 activation quantization to com.microsoft::QMoE v1, enabling native CUDA W8A8 execution while preserving the default weight-only path.
Changes:
- Adds dynamic quantization, native FP8 projections, and FP32 routed reduction without dense expert-weight buffers.
- Validates the supported profile and explicitly rejects unsupported CPU/WebGPU execution.
- Adds numerical, shape-inference, and CUDA-graph tests, plus public documentation.
| File | Description |
|---|---|
| onnxruntime/test/python/transformers/test_qmoe_fp8_cuda.py | Adds dynamic FP8 reference and integration tests. |
| onnxruntime/test/contrib_ops/qmoe_shape_inference_test.cc | Tests supported shapes and invalid metadata. |
| onnxruntime/test/contrib_ops/cuda_kernels/qmoe_fp8_compaction_test.cc | Tests quantization boundaries and routing precision. |
| onnxruntime/core/graph/contrib_ops/contrib_defs.cc | Defines the new attribute and profile validation. |
| onnxruntime/contrib_ops/webgpu/moe/qmoe.h | Rejects dynamic FP8 on WebGPU. |
| onnxruntime/contrib_ops/cuda/moe/qmoe_kernels.h | Declares dynamic FP8 kernel interfaces. |
| onnxruntime/contrib_ops/cuda/moe/qmoe_kernels.cu | Implements quantization, native projections, and reduction. |
| onnxruntime/contrib_ops/cuda/moe/moe_quantization.h | Tracks the dynamic FP8 execution mode. |
| onnxruntime/contrib_ops/cuda/moe/moe_quantization.cc | Integrates validation, scratch allocation, and dispatch. |
| onnxruntime/contrib_ops/cpu/moe/moe_quantization_cpu.cc | Rejects dynamic FP8 on CPU. |
| docs/ContribOperators.md | Documents the new QMoE attribute and semantics. |
| docs/contrib_ops/cuda/moe_qmoe.md | Explains numerical behavior and CUDA requirements. |
Review effort: Balanced
| scales[static_cast<int64_t>(row) * (cols / 128) + group] = scale; | ||
| } | ||
| __syncthreads(); | ||
| const float normalized = fminf(448.0f, fmaxf(-448.0f, value / scale)); |
| template <typename T> | ||
| void LaunchQMoEDynamicFp8Quantize(const T* input, uint8_t* quantized, float* scales, | ||
| int rows, int cols, int activation, cudaStream_t stream) { | ||
| QMoEDynamicFp8QuantizeKernel<<<dim3(rows, cols / 128), 128, 0, stream>>>( |
Register FLOAT only for native FP8 weights/scales, preserve CPU integer placement and existing weight-only modes, and bypass unused A16 runner setup. Match source FP32 div.full normalization at FP8 midpoints; cover FLOAT gate/up range, sparse layouts, CUDA graphs and source parity. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Signed-off-by: Justin Chu <justinchuby@users.noreply.github.com>
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.

Description
Add opt-in
activation_quantization="dynamic_fp8"to the existingcom.microsoft::QMoEv1 operator, supporting FLOAT, FLOAT16 and BFLOAT16 input/router/output tensors. The default"none"preserves weight-only execution.max(amax,1e-10) * float32(1/448)scales, and FP32div.fullnormalization. Both arithmetic choices matter at FP8 midpoints; deterministic regressions cover the one-ULP differences from precise division.mma.sync.m16n8k32instructions and apply each K128 group's scales in FP32. Gate/up outputs use the input type, with no extra FP16/BF16 round before the second quantizer. FLOAT gate/up stays FP32, including values above 65504. Down projection, routed products and top-k reduction remain FP32 through the final output cast. Routing coefficients round to the input type before multiplication.Initial profile: FP32/FP16/BF16 inputs; FP32 weight scales;
quant_type="fp8",expert_weight_bits=8,block_size=128; 128-aligned hidden/intermediate dimensions; standard SiLU/SwiGLU; native SM89+ kernel; at most 65,535 token/expert pairs per invocation. Biases, supplied activation/global scales, zero points androuter_weightsare rejected. CPU/WebGPU reject the mode explicitly. Requires the existingonnxruntime_USE_FP8_QMOE=ONbuild option; disabling fused execution is an error, not a W8A16 fallback.Validation
Fresh isolated Linux Release build, CUDA 13.3 and H200 SM90. No performance, non-H200 hardware, or full-checkpoint qualification is claimed here.
Build command (from the source directory, with
ROOT=/tmp/ort-drama3-w8a8-c6396368andPY=$ROOT/venv/bin/python) — passed; the final FLOAT/normalization changes were incrementally rebuilt and the wheel reinstalled:Using
TEST=$ROOT/src/onnxruntime/test/python/transformers/test_qmoe_fp8_cuda.py, the following commands ran from$ROOTon the final plain-QMoE build:The skipped cases include opt-in large shapes (run separately above) and intentionally unsupported FLOAT/weight-only CUDA graph combinations. The suite also verifies that FLOAT/integer QMoE remains on CPU with CUDA first in the provider list.
From the build output directory:
python -m ruff check onnxruntime/test/python/transformers/test_qmoe_fp8_cuda.py,python -m ruff format --check onnxruntime/test/python/transformers/test_qmoe_fp8_cuda.py, andgit diff --check— passed. Changed C++ lines were formatted with clang-format 18. All 17 public QMoE attribute descriptions match the freshly built schema.A separate six-case comparison against the pinned Fish Speech FP8 implementation (
541566932abbaebcb0a40e562a1f846738383f38) passed decode (1 token) and prefill (33 tokens) for all three dtypes at the full expert dimensions above with seeded synthetic weights. FLOAT used strictatol=2e-6, rtol=2e-5; maximum absolute error was 8.94e-8 for decode and 2.384e-7 for prefill. FLOAT16 maximum error was 4.883e-4; BFLOAT16 was bit-identical for decode and had maximum error 1.221e-4 for prefill. This verifies operator/source semantics, not full-model or checkpoint fidelity. No assertions were loosened to qualify FLOAT; its normalization midpoint discrepancy was fixed and regression-tested.Baseline qualification: the broader initial run had five failures. Recompiling baseline HEAD's QMoE host implementation and relinking against the identical dense kernels reproduced all five: four existing dense-fallback SiLU precision/overflow comparisons and one compact-build BF16 INT8 runner availability limitation. These unchanged paths remain excluded from the scoped regression command above, rather than hiding their failures behind a passing total.
Motivation and Context
Existing block-FP8 QMoE preserves weight storage but uses FP16/BF16 activations. It does not reproduce models that dynamically quantize activations before both expert projections. Extend the general operator rather than introduce a model-specific op, dequantize complete expert banks, approximate W8A8 using A16 GEMM, or change the existing W8A16 default. FLOAT input/output support additionally permits FP32 residual/tower streams without rounding gate/up intermediates to a low dtype.