Skip to content

[CUDA] Add GatherPacked, MatMulPacked, and MoEPacked contrib ops for block-packed weights - #33953

Open
Tianlei Wu (tianleiwu) wants to merge 10 commits into
mainfrom
tlwu/qwen38-gguf
Open

Tianlei Wu (tianleiwu) wants to merge 10 commits into
mainfrom
tlwu/qwen38-gguf

Conversation

@tianleiwu

@tianleiwu Tianlei Wu (tianleiwu) commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

Description

Adds three com.microsoft contrib operators—GatherPacked (CPU and CUDA), MatMulPacked (CUDA), and MoEPacked (CUDA)—that consume 2D and 3D block-packed quantized weight blobs (such as GGUF IQ2_XXS, IQ2_XS, IQ2_S, IQ3_XXS, IQ4_NL, IQ4_XS, Q2_0, Q4_0, Q5_0, Q5_K, Q6_K, Q8_0, F16, BF16, F32) directly in their native block layout without offline weight repacking or separate scale tensors. Direct external initializer buffers (#33877) avoid redundant host-side copies while loading .gguf shards. CUDA execution still copies CPU-backed weights to device memory and requires device storage for the packed weights; it does not execute zero-copy from host-backed shards.

Summary of Changes

Contrib Operator Schemas & Registration

File Change
onnxruntime/core/graph/contrib_ops/ms_opset.h Declare GatherPacked, MatMulPacked, and MoEPacked in com.microsoft opset
onnxruntime/core/graph/contrib_ops/contrib_defs.cc Register schemas and shape inference for GatherPacked, MatMulPacked (A, B -> Y with K, N, packed_type), and MoEPacked
onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc Register CPU GatherPacked kernel
onnxruntime/contrib_ops/cuda/cuda_contrib_kernels.cc Register CUDA GatherPacked, MatMulPacked, and MoEPacked kernels

Block-Packed Quantization & Kernel Implementations

File Change
onnxruntime/contrib_ops/cpu/quantization/gguf_quant_tables.h Shared block-quantization lookup tables (iq2xxs_grid, iq2xs_grid, iq2s_grid, iq3xxs_grid, iq3s_grid), block geometry helper (GgmlRowBytes), and CPU row dequantizer
onnxruntime/contrib_ops/cpu/quantization/gather_packed.cc Multi-threaded CPU GatherPacked implementation
onnxruntime/contrib_ops/cuda/quantization/gguf_quant_cuda.cuh CUDA sub-block decoders (DecodeGgufSubBlock8Impl, DecodeGgufSubBlock8SmemLutTpl, DecodeGgufSubBlock16Impl), register-only Iq2xsSignsDev / Iq4NlValueDev helpers, and Tensor Core MmaBf16 wrapper
onnxruntime/contrib_ops/cuda/quantization/gather_packed.{h,cc,cu} CUDA GatherPacked kernel
onnxruntime/contrib_ops/cuda/quantization/matmul_packed.{h,cc,cu} CUDA MatMulPacked kernel with warp-K-split Tensor Core (mma.sync.aligned.m16n8k16) and warp GEMV paths for decode (M <= 8), bounded tiled dequantization + cuBLAS GEMM for prefill (M > 8), and FP32 decoded weights for FP16 prefill when the packed format is not F16
onnxruntime/contrib_ops/cuda/moe/moe_packed.{h,cc,cu} CUDA MoEPacked kernel with fused top-k softmax routing, parallel warp-scan tile bucketing (8 routes/tile in decode, 32 routes/tile in prefill), warp-K-split Tensor Core FC1+SwiGLU in decode, 4-subtile Tensor Core FC1/FC2 in prefill, and 128-bit vectorized finalize
onnxruntime/test/python/transformers/test_gguf_packed_ops.py Unit tests verifying GatherPacked (CPU vs CUDA bit-exactness across 15 formats), MatMulPacked (M = 1, 7, 8, 32), and MoEPacked (M = 4, 48)

Testing

  • Additional regression validation on NVIDIA A100 with CUDA 13: 30 tests and 757 subtests pass both with the normal SM80/SM90a build and with the packed-op kernels compiled as compute_75-only PTX, exercising BF16 SIMT fallback.
  • Configured-device capability probing and restoration were verified on both success and failure; focused lintrunner checks pass.
  • Unit tests:
    python -m unittest onnxruntime/test/python/transformers/test_gguf_packed_ops.py -v
  • End-to-end validation on Qwen3.8-Flash-Next-GGUF (UD-IQ1_S, UD-IQ2_XS, UD-IQ3_XXS) on NVIDIA H200:
    • engine_behaviours.py: 27 / 27 PASS across all three quantization variants.
    • Prefill (TTFT): 0.048–0.052 s at 128 tokens, 0.222–0.244 s at 2K tokens (8,400–9,226 tok/s), and 0.851–0.944 s at 8K tokens (8,682–9,622 tok/s, 17.1x–18.5x speedup over baseline SIMT).
    • Speculative decode (DFlash2 S=8): 20.0–20.9 ms/cycle (183–269 tok/s median, up to 359 tok/s peak).
    • MMLU-Pro (200 items, max_new=8192): 87.50% (UD-IQ1_S), 85.50% (UD-IQ2_XS), 86.00% (UD-IQ3_XXS).

Checklist

  • Tests added/updated
  • No breaking changes (or documented in description)
  • Documentation updated (if applicable)

Copilot AI balanced review requested due to automatic review settings November 6, 2026 23:42
@rui-ren
Rui Ren (rui-ren) self-requested a review November 6, 2026 23:43

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved critical defects can cause initialization crashes, invalid memory reads, CUDA launch failures, and incorrect or undefined packed-operator outputs.

11 open issues
What changed in this PR

Adds block-packed weight support to ONNX Runtime through three contrib operators, enabling embedding lookup, matrix multiplication, and mixture-of-experts execution without offline weight repacking.

Changes:

  • Registers GatherPacked for CPU/CUDA and MatMulPacked and MoEPacked for CUDA.
  • Adds CUDA dequantization, decode, prefill, and expert-routing kernels with Python tests.
  • Extends external-initializer buffer borrowing and device reuse, with loading tests and documentation.
File Description
onnxruntime/​test/​shared_lib/​test_model_loading.cc Adds direct-buffer session-loading coverage.
onnxruntime/​test/​python/​transformers/​test_gguf_packed_ops.py Tests packed formats and decode/prefill paths.
onnxruntime/​test/​framework/​inference_session_test.cc Tests initializer copying and pointer reuse.
onnxruntime/​core/​session/​inference_session.cc Passes the direct-buffer option to graph injection.
onnxruntime/​core/​graph/​graph.cc Extends external-initializer buffer retention.
onnxruntime/​core/​graph/​contrib_ops/​ms_opset.h Adds the three operators to the Microsoft opset.
onnxruntime/​core/​graph/​contrib_ops/​contrib_defs.cc Defines schemas and shape inference.
onnxruntime/​core/​framework/​tensorprotoutils.cc Extends in-memory serialization to small device tensors.
onnxruntime/​core/​framework/​session_state_utils.cc Reuses graph initializers on their planned device.
onnxruntime/​contrib_ops/​cuda/​quantization/​matmul_packed.h Declares packed matrix-multiplication interfaces.
onnxruntime/​contrib_ops/​cuda/​quantization/​matmul_packed.cu Implements decode kernels and cuBLAS prefill.
onnxruntime/​contrib_ops/​cuda/​quantization/​matmul_packed.cc Validates inputs and dispatches CUDA computation.
onnxruntime/​contrib_ops/​cuda/​quantization/​gguf_quant_cuda.cuh Adds shared CUDA decoders and BF16 Tensor Core helpers.
onnxruntime/​contrib_ops/​cuda/​quantization/​gather_packed.h Declares CUDA packed-gather interfaces.
onnxruntime/​contrib_ops/​cuda/​quantization/​gather_packed.cu Implements gathered-row dequantization.
onnxruntime/​contrib_ops/​cuda/​quantization/​gather_packed.cc Registers and validates CUDA GatherPacked.
onnxruntime/​contrib_ops/​cuda/​moe/​moe_packed.h Defines expert-routing parameters and interfaces.
onnxruntime/​contrib_ops/​cuda/​moe/​moe_packed.cu Implements routing, expert computation, and output accumulation.
onnxruntime/​contrib_ops/​cuda/​moe/​moe_packed.cc Validates expert tensors and chunks execution.
onnxruntime/​contrib_ops/​cuda/​cuda_contrib_kernels.cc Registers the three CUDA operators.
onnxruntime/​contrib_ops/​cpu/​quantization/​gather_packed.cc Implements threaded CPU GatherPacked.
onnxruntime/​contrib_ops/​cpu/​cpu_contrib_kernels.cc Registers CPU GatherPacked variants.
include/​onnxruntime/​core/​session/​onnxruntime_session_options_config_keys.h Documents expanded direct-buffer option behavior.
include/​onnxruntime/​core/​graph/​graph.h Adds the graph-injection borrowing parameter.
docs/​Model_Loading_Performance.md Describes external-buffer borrowing and device reuse.

Review effort: Balanced

Comment thread onnxruntime/core/graph/graph.cc Resolved

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can commit the suggested changes from lintrunner.

…block-packed weights

Add com.microsoft::GatherPacked (CPU and CUDA), com.microsoft::MatMulPacked
(CUDA), and com.microsoft::MoEPacked (CUDA) contrib operators that consume
2D/3D block-packed quantized weight blobs (such as GGUF IQ1/IQ2/IQ3/IQ4/Q2_0/Q4/Q5/Q6_K/Q8_0)
directly without offline repacking or split scale tensors.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Disallow output-type split-K reductions in MatMulPacked prefill so FP32 compute cannot silently round intermediate sums to BF16 or FP16. This addresses the Windows A10 packed-operator parity failures while retaining Tensor Core execution.

Preserve the caller's cuBLAS math mode, propagate mode-update failures, and add FP16/BF16 cancellation coverage with a documented prefill numerical contract.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved critical CUDA alignment, dimension-range, and BF16 dispatch defects can cause faults or silently incorrect results, while full-matrix dequantization can exhaust device memory.

16 open issues
11 resolved since last review

Review effort: Max

Validate dimensions before narrowing, support unaligned packed and activation buffers, and bound MatMulPacked prefill scratch. Check compiled BF16 capability, preserve endian-safe decoding and ggml notices, and extend CPU/CUDA regression coverage.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unchecked CPU row-size arithmetic can overflow, and CUDA FP16 prefill can produce infinite outputs even when the correct result is finite.

11 open issues
16 resolved since last review

Review effort: Max

Resolve the generated operator documentation conflicts by retaining both MoEGroupRouter and the packed operators.

Validation: CUDA 13.0 rebuild; 22 packed-operator tests, 14 focused C++ tests, 19 documentation tests; merged registration lint.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Reject oversized packed rows and invalid output types during initialization. Keep FP16 prefill finite, preserve BF16 SIMT fallback on forward-compatible builds, and avoid per-row CPU conversion allocations.

Add independent known answers and multi-block GPU regressions, and clarify packed-weight device-memory requirements.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The JNI-only ORT AAR does not reference legacy support libraries. Avoid loading its large Debug native libraries into Jetifier, which failed NNAPI CI with Java heap space, while retaining Jetifier for other Android test dependencies.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What this PR does

This adds GatherPacked on CPU/CUDA and MatMulPacked and MoEPacked on CUDA. They read native block-packed weights without requiring a separate repacking step or separate scale tensors. That is useful for loading existing GGUF models without keeping an expanded copy of their weights. CUDA still needs device memory for the packed weights; host-buffer borrowing and hybrid CPU/GPU offload are separate parts of the wider work.

What looks good

The current version addresses several earlier concerns: dimension and byte-size checks, unaligned buffers, compiled BF16 capability checks and fallback, bounded MatMul prefill scratch, and FP16 prefill overflow. The tests now have independent known answers for all 15 accepted formats, and CUDA sessions disable CPU fallback. These are meaningful improvements, not just comparisons between two new implementations that could share the same mistake.

What remains before approval

Please fix the CPU GatherPacked global-type-reduction failure described inline and add a regression. The public format contract and the decode/prefill precision differences should also be documented clearly before models start depending on these schemas. The inline comments include two further improvement suggestions: reducing empty MoE launches and broadening the independent IQ fixtures.

For the stack as a whole, please restack and test the actual final candidate. Against this head, I reproduced 16 conflicting paths when combining #33962, including validation, decoders, kernels, schemas and tests. The upper branch contains an older copy of the packed implementation, so conflict resolution needs to preserve the newer fixes here as well as the precision/routing additions. #33991 is explicitly test-only and uses an older bottom snapshot; it does not establish that the current merge stack works together.

Before settling the public operator surface, I would also like a clear comparison with #33940's existing-operator approach. Native packed storage is a reasonable requirement; I am not asking for repacking merely to reuse an operator name, or assuming that the alternative covers every GGUF format here. The question is which new public contracts are necessary, while keeping decoding, routing and expert execution reusable.

Reviewed f5f7aee with three independent reviewers. I reproduced the registration filtering failure, the numerical example and tile-bound calculations, and checked the stack conflicts. I did not run a full ORT build, GPU runtime tests, sanitizers or latency benchmarks.

Preserve GatherPacked output/index pairs in reduced builds and bound MoE launches by the route count without changing workspace layout.

Define stable public packed layouts and decode/prefill rounding behavior, with independent IQ known answers and dispatch-boundary regressions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@tianleiwu

Copy link
Copy Markdown
Contributor Author

Addressed the five inline items in 9beea24119: pair-aware GatherPacked reduction, the MoE launch clamp, explicit rounding boundaries, a stable packed-format contract, and independent high-index IQ fixtures.

Validation of this lower candidate

  • CUDA 13 Debug Python binding and CUDA provider built successfully; the packaged libraries were verified against the fresh linked outputs.
  • All 33 packed-operator tests passed on the freshly built CPU/CUDA runtime: 811 subtests, zero skips, failures, or errors. All five real-parser reduction tests passed.
  • Focused Ruff, Python formatting, clang-format, and whitespace checks passed. Generated documentation changes are limited to the three packed schemas.
  • For the exact BF16 M=17, k=8, E=512, H=2560, I=7168 workload, an analytic constant-output assertion passed. nsys stats -r cuda_gpu_kern_gb_sum --format csv recorded one FC1 launch with grid (112,136,1) and one FC2 launch with grid (20,136,1): 17,952 combined CTAs. The previous bound calculates to 68,244 CTAs; this is launch-count evidence, not a measured latency improvement.

Comparison with #33940

Contract/capability #33940's existing-operator approach This lower PR
Projection storage Fixed MatMulNBits profiles for offset-UINT8 Q8 and IQ2_XXS, with a separate fixed_scales contract; existing FP4 matmul for NVFP4 Native block blobs with interleaved format metadata and stable IDs for 15 formats
Projection execution Fixed MatMulNBits profiles are bounded BF16 decode paths, rows 1-8 FP16/BF16 decode and prefill, with documented M/N-dependent weight rounding
Packed embeddings No packed Gather implementation in that change set CPU/CUDA GatherPacked
MoE profile Restricted IQ2 QMoE with ReLU-square and preselected or fused sigmoid/bias-corrected routing Three native packed expert blobs, SwiGLU, and softmax/top-k routing

These are not strict supersets: this lower PR does not implement NVFP4 or the Bolt-specific routing contract. Offset-UINT8 Q8 also must not be equated with native Q8_0's signed bytes and interleaved scale.

I agree that native storage does not mathematically require new operator names or repacking merely to reuse existing names. The necessary public contracts are the byte codec/layout and dimensions, activation/output types and rounding, and MoE activation/routing semantics. Those need explicit, stable definitions under either operator surface. #33940 demonstrates the alternative of adding guarded fixed profiles to existing schemas; the three names here expose a uniform native-block interface. Private decoding, routing, and expert execution should remain reusable rather than being separated merely by the public names.

Upper-stack qualification remains outstanding

Re-running read-only git merge-tree --write-tree --name-only --no-messages against this committed lower head and #33962 head ca5bf6a4c1308b4374238b37884d07edf4c3bae5 still reports 16 conflicting paths, including validation, decoder tables, kernels, schemas, and tests.

Rui Ren (@rui-ren): #33962 needs replaying/restacking onto 9beea24119aa03ebe05d03e61e3d5d9d57f8aa44, preserving these newer lower fixes alongside its precision/routing additions, followed by validation of that actual combined candidate. I have not rewritten another author's upper branches or claimed combined-stack validation. #33991 remains an older-snapshot, explicitly test-only integration and does not qualify this updated stack.

iOS device runs have stalled during cold simulator setup before app installation. Reset only the simulator selected by the Detox configuration, then wait for boot and app services without restarting the global CoreSimulator service. Keep app reinstallation, device assertions, and the existing Detox setup deadline unchanged.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@tianleiwu

Tianlei Wu (tianleiwu) commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor Author

Final maintenance verification at 69a0a3ca25: the complete, freshly paginated all-20 check/review audit at2026-11-09T16:41:24Z verifies this PR's complete current-head CI green (101 SUCCESS, one NEUTRAL, no pending or failed contexts) and zero unresolved feedback. The PR remains open/non-draft. Main advanced to 924b50b0166d278cdc62fd39e0ce1a4c5cebe06c when AMX#33258 was merged during processing; an actual three-way Git merge-tree check confirms this exact Packed head merges with that new main without conflicts. Normal reviewer approval remains external; no merge or self-approval was performed by this maintenance work.

The native reviewer fixes in 9beea24119 and targeted simulator preparation in ccea73c62b are preserved. The preceding9beea iOS report had zero executed cases after cold boot/setup stalled. Its two-file preparation repair passed all nine actual Packed YAML/Node-config regressions, only prepares the uniquely selected simulator and preserves the240-second Detox deadline, reinstall, cleanup and assertions without restarting CoreSimulator globally.

The subsequent exactccea React Native42622402833 genuinely executed three cases: MNIST/Basic Types passed, then EPContext reload failed after a native signal11 in the AsyncWorker::toPromise background-thread lambda+56. Artifact13679289029 preserves that stack; it identifies the worker thread, not a disassembled source instruction. This executed-device crash was not relabeled as infrastructure or blindly retried.

The identical original39a header has an independently reproduced post-dispatch UAF: resolve/reject dispatch can release the last worker owner before the background lambda subsequently accesses raw this through onFinished(). The four-file69a repair moves completion before dispatch, preserves exactly-once completion on exceptions/cancellation, adds settlement-completion assertions and negative component tests, and documents the ordering. No Packed operator/provider/kernel or preceding reviewer-fix code was changed.

Fresh local validation on actual Packed69a/f58:640 real pinned-Hermes/JSI ASan+LSan lifetime scenarios in two independent processes, covering resolve/reject/cancellation/Env invalidation/dispatch exceptions, with compiler dependencies verifying the actual checkout header; all five focused EPContext component tests; configured native policy1/1; and scoped configured C++ formatting, repository TSX ESLint and repository-locked Prettier checks. This is native lifecycle validation, not a claim that the complete optional Packed/device matrix ran locally.

Decisive fresh device proof: exact69a React Native42634651129 completed SUCCESS. Android artifact13684063653 and iOS artifact13686716401 each explicitly identify that run/head and contain exactly three actual passing MNIST, Basic Types and EPContext cases, no failures/errors/skips, and the unchanged14/14 checks passed assertion in device logs/traces. The originally failing iOS EPContext case now passes on the repaired current head. All other current-head integrations, including Windows CUDA42634651253 and the final WebGPU packaging/static-plugin gates, have also succeeded. No tests, tolerances, runtime assertions or teardown checks were bypassed.

Run the native completion hook before dispatch can release the final worker reference. Preserve exactly-once completion when dispatch throws, and assert completion at promise settlement in the EPContext bridge regressions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

@rui-ren Rui Ren (rui-ren) left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants