Skip to content

[CUDA] Add fast INT3 QMoE kernels and offline SM80 INT2/INT4 prepack support - #33569

Merged
David Fan (jiafatom) merged 6 commits into
mainfrom
tlwu/20261023/qmoe-int3-int2-prepack-cuda
Oct 25, 2026
Merged

David Fan (jiafatom) merged 6 commits into
mainfrom
tlwu/20261023/qmoe-int3-int2-prepack-cuda

Conversation

@tianleiwu

@tianleiwu Tianlei Wu (tianleiwu) commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Description

Adds fast CUDA execution kernels for INT3 and mixed-width (FC1, FC2) ∈ {2, 3, 4} × {2, 3, 4} com.microsoft::QMoE projections, and extends the SM80 (fpA_intB) packed INT2 and mixed INT2/INT4 execution paths (moe_gemv decode and RunInt2MoePrefill grouped GEMM prefill) and single-CTA fused routing to support offline prepacked weights (weights_prepacked = 1), block_size = 32 2-scale GEMV dequantization, num_experts <= 512, k = 10, expanded_num_rows <= 80, and kFusedRouteMaxRows = 8 ($S \le 8$, 7-draft DFlash2 speculative decoding).

Summary of Changes

Fast INT3 & Mixed-Width QMoE CUDA Kernels

File Change
onnxruntime/contrib_ops/cuda/moe/qmoe_kernels.h Declares Int3Projection, Int3MoeParams, LaunchInt3Moe, and ValidateInt3MoeContent.
onnxruntime/contrib_ops/cuda/moe/qmoe_kernels.cu Implements Unpack8CenteredCodes / Unpack8CenteredCodes128 (aligned 32-bit __ldg + hardware __funnelshift_r 24-bit 8-element INT3 unpacking), FusedFc1FastKernel (shared-memory s_input staging + 2-column (gate, up) register reuse per warp), Fc2RouteFastKernel (parallel-route gridDim.y = rows * top_k launch + shared-memory s_act staging + 16-lane half-warp 2-column tiling), and FinalizeRoutesFastKernel.
onnxruntime/contrib_ops/cuda/moe/moe_quantization.h Adds ComputeInt3, has_int3_, and per-instance int3_content_validated_ atomic flag.
onnxruntime/contrib_ops/cuda/moe/moe_quantization.cc Enables expert_weight_bits and fc{1,2,3}_expert_weight_bits in {2, 3, 4, 8}, dispatches INT3 projections to ComputeInt3, and caches one-time content validation while skipping host synchronization during CUDA Graph capture.

Offline SM80 INT2 / Mixed INT2/INT4 Prepacking (weights_prepacked = 1), block_size = 32 Decode moe_gemv, & $S \le 8$ Routing

File Change
onnxruntime/contrib_ops/cuda/llm/moe_gemm/moe_gemv.h Raises kMaxProfiledExpandedRows from 8 to 80 so $S \le 8, k = 10$ (expanded_num_rows <= 80) dispatches to moe_gemv.
onnxruntime/contrib_ops/cuda/moe/qmoe_fused_route.cuh Widens kFusedRouteMaxRows from 4 to 8 (kFusedRouteThreads = 256, s_sorted[256]) so single-CTA FusedSoftmaxTopKMapsKernel handles up to 8 rows ($S = 8$ DFlash2 verification).
onnxruntime/contrib_ops/cuda/llm/moe_gemm/moe_gemv.cu Adds dequantize_and_pack_to_vec2<Details, GroupSize, TypeA> and scales_iterator_hi across all 4 moe_gemv kernels so Int2DetailsW (StepK = 64) supports 2-scale GroupSize == 32 dequantization alongside GroupSize == 64 and 128.
onnxruntime/contrib_ops/cuda/moe/moe_quantization.cc Accepts weights_prepacked = 1 for INT2 ((2, 2)) and mixed INT2/INT4 ((2, 4), (4, 2)), consuming offline SM80 fpA_intB weight tensors directly in-place (packed_int_fc1, packed_int_fc2) for block_size ∈ {32, 64, 128}, num_experts <= 512, and k ∈ {1, 2, 4, 6, 8, 10} while releasing untransposed [E, N, K_blocks] scales in PrePack.
docs/contrib_ops/cuda/moe_qmoe.md Documents INT3 QMoE CUDA execution and offline SM80-prepacked INT2 / mixed INT2/INT4 support.
onnxruntime/test/contrib_ops/moe_test.cc Adds QMoETest_MixedWidthCudaPackedDecodeBlock32 and QMoETest_Int3AndMixedWidthCudaSwiGLU unit tests.

Testing

  • Verified QMoETest_MixedWidthCudaPackedDecodeBlock32 and QMoETest_Int3AndMixedWidthCudaSwiGLU across (3, 3), (3, 4), (4, 3), (2, 3), (2, 4), (4, 2), and (2, 2) with block_size ∈ {32, 64} and num_rows ∈ {1, 4, 64}.
  • Evaluated $S = 8$ (7-draft DFlash2) and 9 lower-precision 48-layer 512-expert QMoE model packages on the full 800-sample MMLU-Pro benchmark (max_new = 8192) on NVIDIA H200 GPUs (361.14 tok/s median, 411.32 tok/s P90, 453.30 tok/s max at $S = 8$).

…support

- Add CUDA QMoE execution for INT3 and mixed-width {2,3,4}x{2,3,4} projections
  (FusedFc1FastKernel with aligned uint32 __ldg + __funnelshift_r unpacking and
  shared-memory input staging, parallel-route Fc2RouteFastKernel, and
  FinalizeRoutesFastKernel) with one-time validation caching for CUDA Graph capture.
- Support offline SM80 fpA_intB prepacked weights (weights_prepacked=1) and
  block_size=32 2-scale GEMV dequantization for INT2 and mixed INT2/INT4 QMoE
  up to 512 experts, top_k=10, and expanded_num_rows <= 64.
- Ensure QMoE PrePack helpers fall back to the kernel GPU allocator when the
  supplied initializer allocator is on CPU.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 23, 2026 22:07

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 scale-validation, packed-layout fallback, and numerical-consistency defects can cause out-of-bounds reads or incorrect results.

7 open issues
What changed in this PR

This PR extends CUDA QMoE execution with INT3 and mixed-width kernels, plus offline SM80-prepacked INT2/INT4 support for lower-memory MoE inference.

Changes:

  • Adds direct INT3 projection kernels, dispatch, and content validation.
  • Expands packed INT2/INT4 execution to block-size-32 dequantization, larger routing configurations, and offline-packed weights.
  • Adds CUDA test cases and documents supported layouts and execution paths.
File Description
onnxruntime/​test/​contrib_ops/​moe_test.cc Adds block-size-32 and INT3 mixed-width tests.
onnxruntime/​contrib_ops/​cuda/​moe/​qmoe_kernels.h Declares INT3 parameters and launch interfaces.
onnxruntime/​contrib_ops/​cuda/​moe/​qmoe_kernels.cu Implements INT3 scalar, optimized, and validation kernels.
onnxruntime/​contrib_ops/​cuda/​moe/​moe_quantization.h Adds INT3 dispatch and validation state.
onnxruntime/​contrib_ops/​cuda/​moe/​moe_quantization.cc Integrates INT3 execution and offline-packed INT2/INT4 handling.
onnxruntime/​contrib_ops/​cuda/​llm/​moe_gemm/​moe_gemv.h Raises the expanded-row dispatch threshold to 64.
onnxruntime/​contrib_ops/​cuda/​llm/​moe_gemm/​moe_gemv.cu Adds two-scale block-size-32 INT2 dequantization.
docs/​contrib_ops/​cuda/​moe_qmoe.md Documents INT3 and offline-packed integer support.

Review effort: Balanced

@tianleiwu
Tianlei Wu (tianleiwu) marked this pull request as draft October 23, 2026 22:22
- Validate block-wise scale shapes for offline-prepacked INT2/mixed weights
  by caching the shape before PrePack releases the scales.
- Reject offline-prepacked inputs that fall to the dense dequantize fallback.
- Cache INT3 content validation only for constant initializers so runtime-fed
  scales/zero points are always validated.
- Record expert-selection statistics on the INT3 path.
- Round each dequantized weight to T before accumulation in the fast INT3
  kernels to match the scalar path.
- Add INT3 fast-path parity, invalid zero-point, INT3 tiled-log, and
  weights_prepacked=1 INT2/mixed-width parity and malformed-scale tests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ing to 8 rows

- Raise kMaxProfiledExpandedRows from 64 to 80 in moe_gemv.h so 8-row (S=8, 7-draft DFlash2) verification with top_k=10 uses the fast MoE decode GEMV kernel.
- Widen kFusedRouteMaxRows from 4 to 8 (kFusedRouteThreads=256) in qmoe_fused_route.cuh so single-CTA FusedSoftmaxTopKMapsKernel handles up to 8 rows.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Tianlei Wu (tianleiwu) added a commit that referenced this pull request Oct 24, 2026
…2 QMoE, and PR cleanups

- Extend FoldMid2/FoldTail to head_rows<=8 and pack tokens[4..7] into ring record words 1, 2, 3, and 5.
- Add state_update_capacity (0..8) support to CausalConvWithState and enable state_update_capacity > 0 in QwenS2 GatedDeltaNetDecodeWarpKernel.
- Raise kMaxProfiledExpandedRows to 80 in moe_gemv.h and kFusedRouteMaxRows to 8 in qmoe_fused_route.cuh; add fast INT3 and offline SM80 INT2/INT4 prepack QMoE support.
- Eliminate pq[8][ROWS] local register array in HcUpPreKernel and sync PR cleanups (#33469, #33470, #33471, #33472, #33474, #33548, #33569).

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

@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.

- Add dequantize_and_dot_f32 for AccT==float && CtaM==1 in moe_gemv_kernel,
  moe_gemv_fused_finalize_kernel, and moe_gemv_interleaved_swiglu_kernel to
  eliminate the 128-register tile_w_pack2 array and pack_to_vec2 scalar
  permutations, reducing register usage from 127 to 96 regs (4->5 blocks/SM)
  in FC1 and 80 to 72 regs (6->7 blocks/SM) in FC2 (-536 us/cycle at S=8).
- Dispatch CtaNLargeM/2 (CtaN=2) at M=8 for small-N (N<=2560) Int4 GEMV.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Restore the original import order in test_qmoe_cuda.py so ruff passes.
- Gate the fpA_intB GEMV M=8 small-N dispatch to the measured fp16 INT4
  SM80-interleaved configuration, consistent with the adjacent wave-aware block.
- Fix QMoECudaInt3TiledLogHasOneCounterUpdate to use the swiglu FC1 shapes that
  INT3 requires; the test previously failed shape inference.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@tianleiwu
Tianlei Wu (tianleiwu) marked this pull request as ready for review October 24, 2026 23:18
@jiafatom
David Fan (jiafatom) merged commit bd6010c into main Oct 25, 2026
99 checks passed
@jiafatom
David Fan (jiafatom) deleted the tlwu/20261023/qmoe-int3-int2-prepack-cuda branch October 25, 2026 01:09
Tianlei Wu (tianleiwu) added a commit that referenced this pull request Nov 2, 2026
### Description
Based on `main` after #33452 and #33450 merged (with already-merged
#33569 commits dropped):
1. **SM80-Prepacked INT4 & Raw INT3 Offload Admission
(`moe_quantization.cc`)**: Admits SM80-prepacked INT4
(`quant_type="int", expert_weight_bits=4, weights_prepacked=1,
block_size=32`) and raw INT3 (`quant_type="int", expert_weight_bits=3,
weights_prepacked=0, block_size in {32, 64, 128}`) `com.microsoft::QMoE`
nodes into the v4 MoE offload pipeline.
2. **GPU `Int3MoeParams` Cold-Slot Skipping (`qmoe_kernels.{h,cu}`)**:
Extends `FusedFc1FastKernel`, `Fc2RouteFastKernel`, and
`LaunchInt3Typed` to skip negative cold-miss slots (`expert < 0`),
support `source_route_ids` (`hit_ids`) indirection, and emit
`wave_output` partials.
3. **`int4_bank_mode_` & `int3_bank_mode_` in `QMoEOffload`
(`qmoe_offload.{h,cc}`)**: Adds INT4 and INT3 offload support across
doorbell hybrid decode, adaptive promotion, byte-preserving
`FileExpertStore` host caching (`scales_are_block_major = false` on
CPU), and wave-streaming prefill, along with
`test_v4_int4_and_int3_offload_modes_and_numerical_parity` in
`test_qmoe_offload_v4.py`.

### PR stack

Merge in this order:

1. #33575 - INT4/INT3 QMoE CPU/GPU offload (base: `main`)
2. #33622 - INT4 offload prefill/decode and S=8 doorbell optimizations
(base: #33575)
3. #33629 - Grouped INT3 offload prefill and S=8 streaming optimizations
(base: #33622)
4. #33731 - Tensor-core INT3/INT4 offload decode/prefill (base: #33629)

Prerequisites #33452, #33450, #33689, and #33650 have merged into
`main`.
Retarget each dependent PR to `main` after its predecessors merge.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.

3 participants