Repository navigation
[CUDA] Add source-ordered CUDA IQ2_XXS, Q8_0 and NVFP4 A16 matmul - #33400
Rui Ren (rui-ren) wants to merge 6 commits into
Conversation
Introduce com.microsoft::MatMulGGMLIQ2XXS/v1 for raw serialized 66-byte GGML superblocks. Derive logical dimensions from the packed weight shape, retain canonical grid/sign semantics, and perform decode, multiply, accumulation and optional bias in FP32 before one output cast. The direct packed-weight kernel supports decode and prefill without a full expanded-weight buffer. Register FLOAT/F16/BF16 CUDA kernels and schema validation, document the ABI, preserve the canonical grid's MIT license, and add numerical, precision-order, shape-error and no-CPU-fallback tests. Validated on H100: 18 targeted C++ tests pass, including existing BF16 Q8/NVFP4 coverage. Independent pinned-checkpoint routed/shared row probes pass all three dtypes for decode and prefill. This primitive is not a claim of full-model Bolt support. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: rui-ren <15321482+rui-ren@users.noreply.github.com>
Correct IQ2_XXS to round each complete FP32-decoded weight to the activation type before FP32 products and accumulation. Round the matmul output before native-type bias addition, matching the pinned b12x BF16 A16 source ordering. FLOAT is the identity extension; FLOAT16 is the analogous diagnostic/native-type extension. Add com.microsoft::MatMulGGMLQ8_0/v1 for raw uint8 [N,K/32,34] GGML blocks with the same arithmetic boundaries, preserving serialized FP16 scales without persistent dense expansion. Keep MatMulNBits and NVFP4 kernels unchanged. Update schemas, CUDA registration and operator documentation. Two new precision regressions fail against the preserved df5d runtime. H100 validation passes 29 targeted C++ tests and 36 independent actual-row decoder checks across FLOAT/FP16/BF16, decode/prefill and optional nonzero bias; FP16/BF16 row outputs are bitwise exact. Incremental artifacts relink retained baseline objects, not an all-main rebuild. Full-model integration, performance and 24GB residency remain unqualified. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: rui-ren <15321482+rui-ren@users.noreply.github.com>
Introduce a separate four-input MatMulNVFP4A16 operator without changing existing generic NVFP4 defaults. Keep local E2M1/E4M3 operands native, accumulate in FP32, and apply the global FP32 multiplier before the sole native output conversion for decode and general prefill. Bias remains a separate native Add. Reuse packed small-M execution and use temporary local-only native weights plus FP32 GEMM/epilogue scratch for general M. Add exact source-ordering, cancellation, empty-shape and validation tests, and document the ABI and transient scratch contract. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: rui-ren <15321482+rui-ren@users.noreply.github.com>
Replace hand-authored operator documentation with the exact CPU/CUDA/DML output published by Windows GPU Doc Gen run 38805488540. Restore canonical section/table ordering and schema-derived descriptions so documentation validation no longer reports drift. Only the three new operator sections change; all other operator sections and kernel-table row content are preserved. Runtime source, arithmetic, ABI and frozen validation artifacts remain unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: rui-ren <15321482+rui-ren@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
NVFP4 A16 can select a scalar path that violates its FP32-product contract and overflows for inputs with finite expected outputs.
1 open issue
What changed in this PR
Adds three standalone CUDA quantized matmul operators with explicit rounding contracts, without changing existing MatMulNBits or generic NVFP4 behavior.
Changes:
- Adds direct packed IQ2_XXS and Q8_0 execution.
- Adds NVFP4 A16 with FP32 global scaling after accumulation.
- Registers schemas and kernels, documents contracts, and adds numerical and validation tests.
| File | Description |
|---|---|
| onnxruntime/test/contrib_ops/matmul_nvfp4_a16_test.cc | Tests NVFP4 arithmetic and validation. |
| onnxruntime/test/contrib_ops/matmul_ggml_q8_0_test.cc | Tests Q8 decoding and rounding. |
| onnxruntime/test/contrib_ops/matmul_ggml_iq2_xxs_test.cc | Tests IQ2 decoding and rounding. |
| onnxruntime/core/graph/contrib_ops/ms_opset.h | Registers three operator schemas. |
| onnxruntime/core/graph/contrib_ops/contrib_defs.cc | Defines contracts and shape inference. |
| onnxruntime/contrib_ops/cuda/quantization/matmul_nvfp4_a16.h | Declares NVFP4 operator and helpers. |
| onnxruntime/contrib_ops/cuda/quantization/matmul_nvfp4_a16.cu | Implements local decoding and output scaling. |
| onnxruntime/contrib_ops/cuda/quantization/matmul_nvfp4_a16.cc | Validates inputs and dispatches NVFP4 execution. |
| onnxruntime/contrib_ops/cuda/quantization/matmul_ggml_q8_0.h | Declares Q8 operator and launcher. |
| onnxruntime/contrib_ops/cuda/quantization/matmul_ggml_q8_0.cu | Implements packed Q8 matmul. |
| onnxruntime/contrib_ops/cuda/quantization/matmul_ggml_q8_0.cc | Validates and dispatches Q8 inputs. |
| onnxruntime/contrib_ops/cuda/quantization/matmul_ggml_iq2_xxs.h | Declares IQ2 operator and launcher. |
| onnxruntime/contrib_ops/cuda/quantization/matmul_ggml_iq2_xxs.cu | Implements packed IQ2 matmul. |
| onnxruntime/contrib_ops/cuda/quantization/matmul_ggml_iq2_xxs.cc | Validates and dispatches IQ2 inputs. |
| onnxruntime/contrib_ops/cuda/quantization/iq2_xxs_grid.h | Adds licensed canonical IQ2 grid. |
| onnxruntime/contrib_ops/cuda/cuda_contrib_kernels.cc | Registers CUDA kernels. |
| docs/OperatorKernels.md | Lists supported kernel types. |
| docs/ContribOperators.md | Documents operator formats and arithmetic. |
Review effort: Balanced
Restrict the A16 small-M packed launch to the shared tensor-core eligibility predicate, including K alignment, device support and the process-cached MMA selector. Otherwise retain the existing local-weight decode and FP32 GEMM fallback, avoiding premature FP16/BF16 activation-product rounding or overflow in scalar GEMV. Generic FP4 dispatch behavior is unchanged. Add CUDA overflow and cancellation regressions for K32/64/96 and fresh-process disabled-MMA K128 coverage. Keep the operator documentation aligned with dispatch eligibility. Retained incremental CUDA validation reproduces baseline arithmetic failures and passes all9 default and10 disabled-MMA tests, with zero skips; this is not full-build, resource-peak or model-deployment qualification. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: rui-ren <15321482+rui-ren@users.noreply.github.com>
Use Env::Default().GetEnvironmentVar instead of std::getenv so the fresh-process MMA-disabled regression compiles under MSVC warnings-as-errors. Preserve the exact selector check and skip behavior without suppressing C4996 or changing CUDA arithmetic. Signed-off-by: rui-ren <15321482+rui-ren@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Tianlei Wu (tianleiwu)
left a comment
There was a problem hiding this comment.
LGTM — MatMulGGMLIQ2XXS, MatMulGGMLQ8_0, and MatMulNVFP4A16 schemas, shape inference, CUDA kernels (including the CanUseMatMulBlockQuantizedFp4WeightMma gate), and unit tests look clean.
Do you think we need to wire the |
|
Copilot resolve the merge conflicts in this pull request |
I was about to comment this as well. Otherwise, we will start a pattern of introducing a new MatMul contrib op for each quantization type + activation type. |
| * <a href="#com.microsoft.MatMulBlockQuantizedFp8Weight">com.microsoft.MatMulBlockQuantizedFp8Weight</a> | ||
| * <a href="#com.microsoft.MatMulBnb4">com.microsoft.MatMulBnb4</a> | ||
| * <a href="#com.microsoft.MatMulFpQ4">com.microsoft.MatMulFpQ4</a> | ||
| * <a href="#com.microsoft.MatMulGGMLIQ2XXS">com.microsoft.MatMulGGMLIQ2XXS</a> |
There was a problem hiding this comment.
How does IQ2 differ from the existing 2-bit support in MatMulNBits?
We do not need these operators. Please take a look at my exported bolt models. |

Scope
Add packed GGML IQ2_XXS and Q8_0 CUDA matrix multiplication, with explicit reconstructed-operand/output/bias rounding, and a separate NVFP4 A16 operator with FP32 global scaling after accumulation. This is a standalone primitive contribution, not a full Bolt readiness or performance claim.
Current immutable implementation: 9c9c43f, following 1c1b300 and df5d045. Base: 648f708. Three signed-off commits; no amend, rebase or history rewrite.
Correction to the initial draft
The initial draft decoded weights and retained them as FP32 until final output rounding. Subsequent independent qualification found that the pinned b12x BF16 A16 path rounds each full reconstructed weight to BF16 before multiplication, and adds bias after rounding the matmul output. This is a real source-semantics distinction, not reference noise.
The follow-up corrects IQ2 and adds a separately registered Q8_0 primitive. It does not modify
MatMulNBits, recurrence, or existing NVFP4 kernels. Prior df5d binaries, measurements and mismatch evidence remain separately preserved; they are not relabeled as validation of this correction.The third commit adds a distinct NVFP4 A16 contract, leaving the existing generic NVFP4 operator and its defaults unchanged. Prior df5d and v2 artifacts remain separately preserved.
Operator ABIs
Both operators are
com.microsoft, version 1, no attributes:MatMulGGMLIQ2XXS[...,K], rank>=1[N,K/256,66][N], same type as A[...,N], same type as AMatMulGGMLQ8_0[...,K], rank>=1[N,K/32,34][N], same type as A[...,N], same type as AN and K derive from B; there are no external scales, zero points or padded-tail convention. Empty dimensions, including K=0, are supported.
Arithmetic contract
Let
round_Tdenote round-to-nearest-even to A's element type:Y = round_T(round_T(A @ round_T(dequantize(B)).T) + bias).Without bias, step 5 is omitted. FLOAT rounding is the identity; FLOAT16 is the dtype-generic extension. BF16 boundaries match the pinned b12x A16 source ordering; native b12x execution was not qualified on H100.
The direct packed kernels support decode and prefill without a full-weight expansion, persistent expanded weights, full-matrix dequantization scratch or affine requantization. This is an operator-level statement, not a complete-model footprint claim.
Separate NVFP4 A16 ABI and arithmetic
com.microsoft::MatMulNVFP4A16, version 1, no attributes, exactly four inputs:A[...,K], rank>=1B[N,K/2], low-nibble-first E2M1weight_scale[N,K/16], raw E4M3 bytes, not numerically converted UINT8weight_scale_2K is divisible by 16; Y is
[...,N]in A's native type. Empty dimensions/K=0 are supported. There is no fused bias, activation quantization or reciprocal-scale convention.For all M, including M65+:
Finite local products are exactly representable in FP16/BF16. Do not round the accumulator before applying global scaling, and do not fold the global scale into reconstructed native weights. Optional bias is a separate native Add, after output rounding. BF16 follows explicit b12x A16 source ordering; FP16 is the analogous extension, not a claim about b12x automatic activation-quantization modes.
Small M reuses existing packed CUDA execution without fused bias. General M uses a local-only native weight scratch matrix, FP32 cuBLAS output, and a separate FP32-global/native-output epilogue—not repeated small-M launches. It does not persistently expand weights, but does allocate per-call dense weights and FP32 accumulator scratch for general M. The zero-full-expansion statement above applies to IQ2/Q8, not NVFP4 general M. CUDA12.8+ is required. No hardware FP4-by-FP4 or whole-package24GB claim.
Format fidelity and licensing
IQ2_XXS is not affine INT2. Its 66-byte block holds a little-endian FP16 scale and eight 8-byte records. Each record contains four canonical grid selectors, four seven-bit sign indices with parity-derived eighth signs, and one four-bit local scale:
d * (0.5 + local_scale) * 0.25 * grid[index,j] * sign[j].Byte-wise loads avoid alignment assumptions at the two-byte header and 66-byte stride.
Pinned canonical references:
The canonical grid retains its full MIT license and ggml-author notice in
iq2_xxs_grid.h. Its 2,048 little-endian bytes independently match both the retained ModelOpt codebook and the b12x test oracle:05826b5d3e472a3a78f196be62ac78acf81df0f909626e12ab9fa2a5d490dd54Q8_0 stores a little-endian FP16 scale followed by 32 signed two's-complement INT8 values. We preserve the scale bits and decode
d*qin FP32 before rounding the whole weight. No codebook is required.Independent source-ordering qualification
Pinned distribution: b12x 1.5.0 (SM120/SM121-specific).
_lib/intrinsics.py::q8_0_pair_to_bf16x2: FP16 scale to FP32, signed INT8 to FP32, multiply, thencvt.rn.bf16x2.f32.gemm/blockscaled/_iq2_xs_gemv.py::_decode_eight: FP32 scale/codebook multiply followed by BF16 reconstruction.gemm/bf16_gemv/_kernel.py::_dot_bf16x8: BF16 operands consumed in FP32 products/accumulation.gemm/blockscaled/_linear.py::blockscaled_mm: BF16 matmul output followed byresult.add_(bias).Source SHA256:
_lib/intrinsics.py:7b9a28dd3a4f6339c8fa9c15cdc327b8de23802f4b529cc8aded9b65f97f8e3b_iq2_xs_gemv.py:ac08b0d6fe65ff5de41c5e67bc7e96b86fcca839d29ed00c56eb0bff0fb44322_linear.py:51e0b639705237411b8ee5274e7ae94d3a8cdf8dbe57a9ac07a380b776a3dbfdThe b12x capability gate returns false on H100/SM90; its source validator rejects A16 with
A16 requires SM120/SM121. Public native API import additionally fails because CuTecutlassis absent. No backend was installed/built and no b12x native execution is claimed. Its independent CPU test decoders can be used without importing production kernels.NVFP4 A16 source ordering (same pinned distribution):
_lib/intrinsics.py:2975–3003: E2M1 and E4M3 local native operands, excluding global scale._lib/dense_gemm.py:1719–1721: local NVFP4 decode;:870: FP32 accumulator.gemm/blockscaled/_preparation.py:78–96,278: native operands, FP32 accumulator and global FP32 alpha._lib/dense_gemm.py:1767–1770,3172–3184: load alpha, FP32 accumulator scaling, then native output conversion.gemm/blockscaled/_linear.py:892–902: native output followed by separate native bias addition.Additional source SHA256:
_lib/dense_gemm.py:66c5d91863d0ddff6a2da7db52550df622f3dce4a178f05d02226d4588873f08gemm/blockscaled/_preparation.py:41b8cd87e023dc96263fabda6972447cb994294b674562d02897a77527348addValidation
Physical GPU: NVIDIA H100, SM90.
C++ red/green: 29 passed, zero skips
Two exact rounding regressions were built and run against the preserved df5d provider first: reconstructed-weight cancellation and output-before-bias rounding both failed. Both pass with the correction.
Final targeted suite:
MatMulNBitstests.Bounded actual-checkpoint rows: 36 checks passed
Model:
nvidia/NVIDIA-Nemotron-3.5-Bolt-74B-A9B-24GB, immutable HF revision:c6cc2e8ffa353403213d6b9b18aaf8eb684d705a
Read only 16 rows each from routed IQ2 up-projection, shared IQ2 up-projection and Q8
lm_head. Independently load the unchangedb12x/testing/{iq2_xxs,q8_0}_reference.py::dequantize_blocksCPU functions; round the full reconstructed weights to native dtype; compute FP32 matmul, native output rounding, and optional nonzero bias afterward.Three tensors × three dtypes × M=1/M=15 × bias absent/present = 36 passed.
All 24 FP16/BF16 cases are bitwise exact. Worst FP32 absolute error is 1.5273690223693848e-7. CPU EP fallback and TF32 are disabled; each profile attributes exactly the tested packed operator to CUDA.
This proves selected-row primitive semantics, not full expert/layer/decoder/generation parity or b12x native execution. No checkpoint data, evidence or binaries are committed.
Final v3 C++ and actual-row qualification
56 C++ tests passed, zero skips, on physical H100/GPU1:
60 bounded actual-row CUDA checks passed on the final v3 runtime:
in_proj(K4096) andout_proj(K8192), FP16/BF16, M1/M15/M65, bias absent or separate nonzero native Add. Preserved exact raw E4M3 scales and source scalar FP32 global values. The independently implemented pinned source A16 formula is the oracle; not native b12x execution. All24 outputs are bitwise exact.1.5273690223693848e-7.The existing generic NVFP4 operator remains unchanged. As an explicit contract-distinction diagnostic, its M65 output differs from the new A16 oracle for471/1040 and469/1040 actual in-projection FP16/BF16 outputs, and543/1040 and528/1040 out-projection outputs. Those differences are not masked or relabeled as generic-source parity. Existing generic regressions still pass.
Build-artifact provenance caveat
An isolated v2 overlay compiled the changed IQ2 kernel/test/schema, new Q8 wrapper/kernel/test, environment/opset registrar and CUDA registrar, then relinked local copies of affected archives with retained read-only baseline objects. The original df5d overlay and original build remain unchanged.
Not an all-current-main rebuild. Unchanged runtime/provider objects remain retained baseline objects; unrelated latest-main kernel changes are not qualified.
Frozen v2 core SHA256:
3dabf96ee347cb39e3cd40aed079434c894808c4b63388453dbe09d7ce0e8ec1Frozen v2 CUDA provider SHA256:
cf28d98d0fd6b8de675516930c1cd54c8f50c1833a8a3e7f687f901333838ed4Scoped clang-format and
git diff --checkpassed.The separately frozen v3 overlay adds only new NVFP4 A16 wrapper/kernel/tests and affected schema/opset/environment/CUDA registrations; it retains v2 IQ2/Q8 objects and unchanged generic NVFP4 baseline objects. It is likewise not an all-current-main rebuild. No original/v2 artifacts are replaced. Their recorded artifact hashes were rechecked unchanged.
Frozen v3 core SHA256:
62cc0188ef19594c724b3e7f4047b8908fd5852164d56edfd3e08a24734fb4d8Frozen v3 CUDA provider SHA256:
cfa1cb429a8ddee246ece778aa743533009aa428dbfc6c786be908bbfe2dc8fbAll three commits are signed off with Copilot coauthorship.
Latest upstream main observed at publication:
1988019b6e23ac4def4628cddd603c1bfb504fe8. A read-only three-way merge analysis found no conflict markers. The source branch was not rebased, and this is not build validation of the newer upstream main.Explicit non-claims / remaining integration gates
Existing generic NVFP4 source-ordering differences were independently identified; the new dedicated operator deliberately does not alter the generic operator or claim its source parity. Routing, activation functions, recurrence, processor/media handling, whole-package assembly and generation remain separate integration work.
Draft only; no merge requested. No full-model readiness or performance claim.