Repository navigation
Extend MatMulPacked for native Bolt scaling and BF16 precision - #33962
Conversation
c1b4daf to
0d38b02
Compare
This comment was marked as resolved.
This comment was marked as resolved.
|
The native kernel optimizations are now pushed in #34002 and registered as the Actual #33994-tip baseline versus the new native candidate, real 1028-1663-token The earlier ~92 tok/s private baseline already contained these optimizations: |
There was a problem hiding this comment.
🟡 Changes recommended
The BF16 Tensor Core path can silently return zeros for 75-virtual builds on SM80+ GPUs because dispatch checks hardware capability but not whether the compiled kernel supports BF16 MMA.
3 open issues
What changed in this PR
Extends the packed-weight operators introduced by #33953 with Bolt-specific projection precision controls. The main addition is optional NVFP4 global scaling and complete-weight rounding in CUDA MatMulPacked, while preserving the default dispatch behavior.
Changes:
- Add FP32 global scaling before the output cast and opt-in activation-dtype weight rounding.
- Add contiguous-K BF16 dispatch, alignment-safe packed reads, and precision regression tests.
- Update operator documentation and pin the transformer-test SentencePiece dependency.
| File | Description |
|---|---|
| tools/ci_build/requirements/transformers-test/requirements.txt | Pin SentencePiece for CI protobuf compatibility. |
| onnxruntime/test/python/transformers/test_gguf_packed_ops.py | Test packed operators, formats, and validation. |
| onnxruntime/test/contrib_ops/matmul_packed_precision_test.cc | Cover scaling, rounding, and cancellation behavior. |
| onnxruntime/core/graph/contrib_ops/ms_opset.h | Register packed operator schemas. |
| onnxruntime/core/graph/contrib_ops/contrib_defs.cc | Define packed schemas and projection precision options. |
| onnxruntime/contrib_ops/cuda/quantization/matmul_packed.h | Declare projection parameters and launch interface. |
| onnxruntime/contrib_ops/cuda/quantization/matmul_packed.cu | Implement projection dispatch, rounding, and scaling. |
| onnxruntime/contrib_ops/cuda/quantization/matmul_packed.cc | Validate inputs and allocate projection workspace. |
| onnxruntime/contrib_ops/cuda/quantization/gguf_quant_cuda.cuh | Provide shared packed decoders and alignment-safe reads. |
| onnxruntime/contrib_ops/cuda/quantization/gather_packed.h | Declare CUDA packed-gather interface. |
| onnxruntime/contrib_ops/cuda/quantization/gather_packed.cu | Gather and decode packed rows on CUDA. |
| onnxruntime/contrib_ops/cuda/quantization/gather_packed.cc | Register and validate CUDA packed gather. |
| onnxruntime/contrib_ops/cuda/moe/moe_packed.h | Declare packed-expert parameters and interfaces. |
| onnxruntime/contrib_ops/cuda/moe/moe_packed.cu | Implement packed-expert routing and computation. |
| onnxruntime/contrib_ops/cuda/moe/moe_packed.cc | Validate expert inputs and manage chunked execution. |
| onnxruntime/contrib_ops/cuda/cuda_contrib_kernels.cc | Register CUDA packed kernels. |
| onnxruntime/contrib_ops/cpu/quantization/gather_packed.cc | Implement CPU packed gather and dequantization. |
| onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc | Register CPU packed-gather kernels. |
| docs/OperatorKernels.md | Update packed-kernel listings and WebGPU type tables. |
| docs/ContribOperators.md | Document packed contracts and update GQA descriptions. |
Review effort: Balanced
ca5bf6a to
cdc6f8a
Compare
Rui Ren (rui-ren)
left a comment
There was a problem hiding this comment.
Stack #33967 review — parent 548473fa9111 -> head 9e194f240c12.
Reviewed this layer against its immediate parent, including scaling before the final cast, bounded prefill workspace, opt-in operand rounding, compiled-image BF16 capability gating, and the added tests.
One reproducible test/configuration issue, detailed inline: RoundedBf16ContiguousKCancellation assumes Tensor Core reduction order even when a compute_75-only image correctly selects SIMT. On H100, rebuilding the production MatMulPacked TU with compute_75-only PTX and running that test produces 1 instead of the asserted 2. The previously checked nonzero/complete-operand rounding fallback regressions pass; this is a fixture capability-gating problem, not the old zero-output dispatch bug.
Validation: targeted H100 reproduction using the ABI-matched focused runtime overlay, not a full clean-source ORT build. No source changes made for this review.
Tianlei Wu (tianleiwu)
left a comment
There was a problem hiding this comment.
Reviewed this layer (base main, bottom of stack #33967) at 9e194f240c.
I traced the new NVFP4 decode, the contiguous-K BF16 Tensor Core kernel (operand pairing, decode indices for 64/128-K chunks, tile tails, reduction order), global_scale application on all four routes (GEMV, TC, FP32 prefill, cuBLAS prefill), workspace sizing, and cuBLAS math-mode restore on the error paths. I found no correctness defect in the kernels, and the default dispatch is unchanged.
Should be resolved before merge
- The PTX-only fixture issue in
RoundedBf16ContiguousKCancellationis still open (see the existing thread onmatmul_packed_precision_test.cc; the PR checklist item is unchecked). With a compute_75-only image on SM80+, the SIMT fallback's lane-strided reduction combines the1at k=128 with2^25at k=0 first, giving 1 instead of 2. docs/PackedWeightFormats.mddoes not define NVFP4 or the new rounding/scale semantics, althoughMatMulPackednow acceptspacked_type == 40(inline onmatmul_packed.cc).
Suggestions / nits (inline): default-path global_scale test coverage, UE4M3 scale-byte parity with the pinned reference, and the missing default in LaunchMatMulPackedBf16ContiguousK. Also, docs/ContribOperators.md gains two trailing blank lines at EOF.
1929a70 to
a213df5
Compare
Tianlei Wu (tianleiwu)
left a comment
There was a problem hiding this comment.
Re-review of a213df5bb5.
Verdict: COMMENT — no blocking findings in this layer; all earlier feedback is addressed.
Earlier threads, re-checked on this head
- NVFP4 (ID 40) row, layout,
K % 64and scale arithmetic indocs/PackedWeightFormats.md— fixed (I re-derived0x01→ 2^-9 and0x7e→ 448). global_scaleon the default route — fixed (TestNvfp4GlobalScalenow runs default and opt-in;Nvfp4GlobalScaleLargeNDefaultDecodecovers N = 65536).- Scale bytes with bit 7 set — resolved by contract (bit 7 must be clear, with known-answer tests for
0x00,0x01,0x7e,0x7f). switchwithoutdefaultin the contiguous-K launcher — fixed (ORT_THROW).
I also re-read the epilogues: the FP32 global_scale is applied before the cast on every path (GEMV, warp-K-split tensor-core reduction, cuBLAS prefill through the FP32 output tile + MatMulPackedCastTileKernel, FP32 prefill), and the workspace sizing accounts for the extra FP32 tile.
Independent verification (stack tip 623a725f5c, which contains this layer; Linux, CUDA 13.0, sm_80;sm_90a, Release; A100-SXM4-80GB)
onnxruntime_provider_testbuilds; the stack's 60 provider tests (including this layer's 8MatMulPackedPrecisionTestcases) and 5 internal CUDA tests pass, also 3x in shuffled order. The pre-existing*Packed*suites (655 tests) show 0 failures.compute-sanitizersynccheck, racecheck and initcheck: no findings on the MatMulPacked tests.- CI on this head has no failures so far; the Windows CUDA and wasm build failures in this stack start at #33994.
Suggestions (non-blocking)
- The schema text for
Kstill says "CUDA requires a positive multiple of 32"; NVFP4 needs a multiple of 64 (enforced viaGgmlRowBytes, documented only inPackedWeightFormats.md). A parenthetical "(64 for NVFP4)" keeps the op reference self-consistent. - "
global_scalerequires NVFP4" depends only on the node's inputs andpacked_type, so it can be rejected at session creation (checkinfo.node().InputDefs()in the constructor) rather than at the firstRun.
|
Implemented the session-only MatMulPacked precision policy.
Published atomically across all seven currently unmerged layers, preserving the concurrent #33994 CI fix and newly appended #34065. Native stack #33967 retains all eight members, including merged #33953; nothing merged or auto-merged. Existing exports containing the removed attribute need that attribute removed or re-exporting. Validation uses fresh changed objects over ABI-matched preserved support, not a clean full-source build, full CI, fresh performance measurements or full-model generation qualification. |
2936957 to
9b1132c
Compare
9b1132c to
f51abbc
Compare
Tianlei Wu (tianleiwu)
left a comment
There was a problem hiding this comment.
Reviewed f51abbca077f against base 3bde254157f3, including all 11 changed files and the latest session-only precision policy.
No new actionable findings. I traced default/unset/"0" rounding and the "1" GEMV relaxation, complete-weight rounding in FP16's FP32 prefill scratch, BF16 contiguous-K operand mapping and compiled-image capability gating, and NVFP4 scaling before the output cast across decode and tiled prefill. Workspace sizing and tile tails are consistent with those routes.
Independent validation: compiled this head's unchanged production matmul_packed.cu and decoder header with CUDA 13.0, then ran a standalone direct-launch harness on H200:
- 248 cases passed with native sm_90a, with BF16 Tensor Core support reported as available.
- 248 cases passed with compute_75-only PTX, with BF16 Tensor Core support correctly reported as unavailable.
cuobjdumpconfirmed sm_75 PTX and no native device ELF. - Covered F32/Q8_0/IQ2_XXS/NVFP4 packed weights, FP16/BF16 activations, both rounding settings, M=1/7/8/9 plus M=129 tile crossing, K=64/128/256, and N=17/4099/8192/65536. NVFP4 cases included the FP32 global scale; comparisons used dtype-appropriate tolerances.
This harness passes dispatch parameters directly and reuses existing ORT support libraries; it does not exercise model/session initialization or constitute a clean full-source build, the repository's complete test suite, or full-CI qualification.
The two suggestions in the previous review remain non-blocking: clarify NVFP4's K multiple in the schema text, and reject a supplied global_scale for non-NVFP4 nodes at kernel creation. No duplicate inline threads added.
f9bd807 to
1ebecd3
Compare
Keep integer packed_type and existing projection defaults. Add opt-in complete-operand rounding and an optional FP32 NVFP4 global multiplier applied before the final activation cast. Decode native NVFP4 blocks and cover decode/prefill boundaries and Q8/IQ2 cancellation fixtures. Production-flag translation units compile. Runtime numerical, generation and throughput qualification remain pending. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: rui-ren <ruiren1225@gmail.com>
Retain the source-compatible 64/128-K partitions and tensor-core operand order when complete-weight rounding is requested. Keep the coworker default path unchanged and cover a qualified cancellation counterexample across short row counts. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: rui-ren <ruiren1225@gmail.com>
Allow the implemented CUDA NVFP4 format through the supported-type guard without changing CPU support. Separate unit terms from large MMA partials in the cancellation regression so its exact oracle is valid on Ampere and Hopper. Pin the test-only sentencepiece dependency to a protobuf-6-compatible release and refresh canonical generated operator docs. Ten current packed CUDA regressions pass with no skips in the bounded production-object test overlay; full source/model qualification remains separate. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: rui-ren <ruiren1225@gmail.com>
Combine native NVFP4 scaling and operand rounding with upstream bounded workspace, finite FP16 prefill, alignment-safe loads, and compiled BF16 capability checks. Cover scaled output across both prefill tile boundaries and describe the opt-in rounding contract accurately. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: f4d0e1a3-a389-4cc8-9c85-0fd0e3ffce5b Signed-off-by: rui-ren <ruiren1225@gmail.com>
Cover short and wide Q8 projections with nonzero rounded-weight outputs across generic and contiguous-K dispatch. Running this regression with compute_75-only PTX on SM80+ catches hardware-only BF16 MMA capability checks that otherwise return zeros. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: f4d0e1a3-a389-4cc8-9c85-0fd0e3ffce5b Signed-off-by: rui-ren <ruiren1225@gmail.com>
Define CUDA-only NVFP4 support, valid scale bytes and operand rounding. Exercise default and opt-in scaling including the large-N path and scale boundary known answers. Fail unsupported contiguous dispatch explicitly. Keep cancellation terms compatible with TC and PTX SIMT reduction trees. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: f4d0e1a3-a389-4cc8-9c85-0fd0e3ffce5b Signed-off-by: rui-ren <ruiren1225@gmail.com>
Remove the model attribute and resolve the strict 0/1 CUDA session policy once during kernel construction. Keep complete activation-dtype operand rounding in every GEMM/prefill path, including FP32 scratch, while allowing eligible GEMV kernels to skip it explicitly. Preserve NVFP4 output-scale placement and compiled BF16 kernel dispatch guards. Exercise missing/default, explicit 0/1, invalid policies, FP16 FP32-scratch prefill, BF16 dispatch boundaries and NVFP4 scale ordering. Synchronize the schema and operator documentation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: f4d0e1a3-a389-4cc8-9c85-0fd0e3ffce5b Signed-off-by: rui-ren <ruiren1225@gmail.com>
…tract Update all-format and dispatch-boundary references to activation-rounded weights. Exercise the session-only GEMV opt-out, including FP16 weights that overflow on rounding: default GEMV and every prefill retain that rounding while eligible explicitly relaxed GEMV remains finite. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: f4d0e1a3-a389-4cc8-9c85-0fd0e3ffce5b Signed-off-by: rui-ren <ruiren1225@gmail.com>
Round decoded weights in the unaligned-buffer oracle for every activation dtype and row count, matching the default CUDA operand contract. Preserve the existing numerical tolerances. An exact-source H100 runtime overlay reproduces 29 failures before the fix and passes all 492 subcases across the corrected and related precision tests afterward, with no skips. Refresh the Android minimal-baseline budget from 1,610,752 to 1,610,778 section bytes. Both the exact upstream main job 129638086415 and PR job 129642809219 already produce identical sizes for all 28 ELF sections, so this is inherited baseline growth rather than packed-projection overhead. Add only the measured 26 bytes, with no extra headroom; the size guard still rejects one byte over budget. Signed-off-by: rui-ren <ruiren1225@gmail.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1ebecd3 to
f0205d7
Compare


Description
Extends
com.microsoft::MatMulPacked(CUDA) with the scaling and operand-precisioncontract needed by Nemotron Bolt, without repacking native weights. The optional scalar
FP32 NVFP4
global_scaleis applied after FP32 accumulation and before the output cast.Complete decoded weights now round to the activation dtype by default. The precision
policy is session-only:
ep.cuda.matmul_packed_allow_unrounded_gemv="0"(or unset)retains rounding, while
"1"permits eligible GEMV kernels to skip it. Tensor-corekernels may still round. GEMM/prefill always rounds, including with FP32 scratch buffers.
Only
"0"/"1"are accepted; invalid values fail kernel/session initialization.The
dequantize_to_activation_dtypenode attribute is removed. Existing exports carryingthat attribute must remove it or be regenerated; it is no longer an operator attribute.
The BF16 decode path preserves contiguous K partitions and MMA operand ordering.
Tensor Core dispatch checks compiled-image support as well as GPU capability so
compute_75-only PTX correctly uses SIMT on newer GPUs. Prefill retains bounded tiled
dequantization/GEMM, including the FP16 FP32-accumulation path.
Dependency: first follow-up to merged #33953; this PR now targets
main.Part of native stack #33967.
No CPU-format expansion, weight repacking, or GenAI source change.
Summary of Changes
Operator Contract and Documentation
onnxruntime/core/graph/contrib_ops/contrib_defs.ccdocs/ContribOperators.md, docs/PackedWeightFormats.mdCUDA Implementation and Regression Tests
The session key is declared in
include/onnxruntime/core/session/onnxruntime_session_options_config_keys.hand resolved once per kernel, without an environment alias or model attribute.onnxruntime/contrib_ops/cuda/quantization/matmul_packed.{cc,h,cu}onnxruntime/contrib_ops/cuda/quantization/gguf_quant_cuda.cuhonnxruntime/test/contrib_ops/matmul_packed_precision_test.ccTesting
Session-policy update: owning head
2936957a4d9bfccaa070429938446c0c1482228b; optimized stack tipa3f2076b65e0d1a5c7e9b82165ffab014e233876.covering all packed formats, session-policy dispatch boundaries, and FP16 operand overflow.
including byte-offset IOBinding fallbacks, and 1 actual internal CUDA cancellation test.
documentation matches the production schema. CLANGFORMAT, Ruff/format and whitespace checks pass.
Runtime checks use fresh policy objects and the exact MatMulPacked schema registration over
ABI-matched preserved support. Not a clean full-source build, full-CI approval, independent
HF parity, performance remeasurement, or new end-to-end model qualification.
NVFP4 global-scale placement and unrelated MoE/router ownership remain unchanged.
Historical measurements and qualification evidence (not current-head acceptance)
Earlier review qualification (historical)
Review-fix snapshot:
1929a7099606c61f00a40f2e199007ee090eda3e.Documented NVFP4 ID/layout, bit7-clear scale domain, scale edge cases, K geometry, CUDA-only availability and FP32 global-scale placement. Added default/opt-in scale tests, large-N default dispatch coverage, and an explicit failing switch default. Revised the cancellation fixture so both the native TC and compute75 PTX SIMT paths assert 2.
Owning-layer validation: 8 native + 8 compute75-only PTX projection tests; zero failures/skips.
Integrated tip regression: 51 C++ provider/schema tests, 5 actual internal CUDA tests and 2 Python IOBinding tests passed, zero failures/skips. All owning-layer provider cases are retained. The final overlay recompiles the changed CUDA/host/schema/test sources over ABI-matched preserved ORT support; not a clean full-source build, full-CI approval, independent HF parity or new end-to-end qualification. Targeted configured clang-format/Ruff and whitespace checks passed.
These previously reported results are retained for provenance. They have not been rerun on the rebased head above; earlier CI/build status and benchmark results do not establish current-head CI, accuracy, or merge readiness.
Historical policy labels: the table predates this session-policy change. 'Default' below denotes the former unrounded-GEMV policy (now permitted by the session opt-out); 'Precision opt-in' denotes rounded operands (now the default). The old attribute is no longer available. These timings have not been remeasured.
Measured CUDA kernel latency - 2026-11-07
BF16 inputs/output and FP32 accumulation, Q8_0/NVFP4 packed weights; NVFP4 global scale 0.75. Default dispatch is compared with
dequantize_to_activation_dtype=true. For rows >8 both paths use full dequantization plus cuBLAS (and the NVFP4 output-scale pass).This is a precision option, not a universal speedup: single-row latency regresses in all four shapes/formats; Q8_0 eight-row dispatch improves, NVFP4 eight-row results depend on shape, and 32-row results are approximately unchanged. Current operand rounding is now the default. All 12 synthetic cases have bitwise-equal complete outputs in both runs; these representable synthetic scales do not replace the separate adversarial precision tests.
H100 80GB HBM3, CUDA 12.8.93, driver 580.159.04, GCC 11.4.0. CUDA-event median per dispatch; 64 dispatches per captured CUDA graph, 50 warmups, three graph warmups, and 15 paired timed batches alternating AB/BA. Four rotating allocation banks contain deterministic synthetic weights (seed 617); this is not a cold-cache guarantee or a checkpoint-weight benchmark. Raw p95 values describe batch-average latency, not individual-invocation tails. No profiler runs in the timing window. The assigned GPU had empty admission, one stable compute client during execution, and clean release afterward; clocks were not locked. Two independent 39-case runs passed, including the published replay runner.
Scope: same-build production CUDA dispatch alternatives, not historical parent-commit binaries. The measured runtime is an isolated CUDA provider relink with preserved core/support, not a clean full-source ORT build or end-to-end model qualification. Each measured operator's CUDA source hash matches its owning PR head. A clean source build is a separate replication variant and must be measured again. Kernel-only timings do not establish model TTFT/tok/s, MMLU/LCB, speculative integration, or full-model readiness.
Complete primary/repeat results, numerical metrics, source hashes and benchmark code. All 39 cases passed in both supervised runs; full-model work remains paused. No source push, stack change, merge, auto-merge or ready-for-review promotion is included.
Checklist