Skip to content

Add WebGPU INT2 and mixed-width QMoE support - #32878

Merged
Sayan Shaw (sayanshaw24) merged 4 commits into
mainfrom
sayanshaw/qmoe-int2-webgpu
Oct 5, 2026
Merged

Sayan Shaw (sayanshaw24) merged 4 commits into
mainfrom
sayanshaw/qmoe-int2-webgpu

Conversation

@sayanshaw24

@sayanshaw24 Sayan Shaw (sayanshaw24) commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Description

Adds WebGPU execution support for INT2 and mixed-width integer QMoE, following the operator contract introduced for CUDA in #32761.

  • Accepts 2-bit expert weights on WebGPU and passes FC1-, FC2-, and FC3-specific bit widths to the existing MatMulNBits implementation.
  • Supports inherited INT2 and mixed INT2/INT4 widths through direct and indirect expert routing, including separate non-fused SwiGLU FC3.
  • Rejects FC1, FC2, and optional FC3 packed rows or effective blocks that cannot be indexed in 32-bit words.

The underlying WebGPU MatMulNBits shaders already implement 2-bit unpacking, so no new shader path is required.

Validation

  • 16 focused H200/Vulkan WebGPU QMoE tests passed, including two-expert k=2 indirect selection, different experts across two tokens, separate FC3, and unaligned-row rejection.
  • Release provider build, formatting, and diff checks passed.
  • Final Android minimal binary-size check passed on the merged head; the earlier 509-byte overage did not recur.

Follow-up

The original mixed-width tests distinguished experts using different scales but identical packed-weight values. The separately reviewable follow-up in #33058 gives each expert distinct packed-weight values and updates the numerical reference to cover weight-offset errors. The new tests passed on H200; this follow-up is not part of the merged #32878 commit.

@sayanshaw24
Sayan Shaw (sayanshaw24) marked this pull request as ready for review September 29, 2026 17:40
Copilot AI balanced review requested due to automatic review settings September 29, 2026 17:40

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.

Copilot review overview

🟡 Changes recommended

Homogeneous INT2, FC3-specific widths, and multi-token mixed-width execution lack direct WebGPU coverage.

Review effort: Balanced
Findings: 3 Low severity

Open (3)
What changed in this PR

Adds WebGPU INT2 and mixed-width QMoE execution support.

Changes:

  • Accepts 2-bit expert weights.
  • Dispatches FC1/FC2/FC3 with independent bit widths.
  • Adds single-token mixed-width numerical validation.
File Description
qmoe.h Permits INT2 weights.
qmoe.cc Applies projection-specific bit widths.
moe_test.cc Tests INT2 FC1 with INT4 FC2.

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread onnxruntime/contrib_ops/webgpu/moe/qmoe.cc
Comment thread onnxruntime/contrib_ops/webgpu/moe/qmoe.cc
Comment thread onnxruntime/contrib_ops/webgpu/moe/qmoe.h

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.

Copilot review overview

🟡 Changes recommended

Some valid INT2 dimensions can silently produce incorrect results, and several changed execution branches remain untested.

Review effort: Balanced
Findings: 1 High severity · 4 Low severity

Open (5)

Comment thread onnxruntime/contrib_ops/webgpu/moe/qmoe.h
Comment thread onnxruntime/test/providers/webgpu/contrib_ops/moe_test.cc Outdated
Comment thread onnxruntime/test/providers/webgpu/contrib_ops/moe_test.cc Outdated
Reject partial u32-packed FC1, FC2, and FC3 rows and blocks before MatMulNBits dispatch. Test inherited INT2, routed mixed-width decode and multi-token experts, and independent FC3 widths.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use the non-fused SwiGLU branch when testing independent INT2 FC3 packing and its alignment rejection.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@kunal-vaishnavi
kunal-vaishnavi requested a balanced review from Copilot October 5, 2026 22:06
Comment thread onnxruntime/test/providers/webgpu/contrib_ops/moe_test.cc
@sayanshaw24
Sayan Shaw (sayanshaw24) enabled auto-merge (squash) October 5, 2026 22:19
@sayanshaw24
Sayan Shaw (sayanshaw24) merged commit ec1d573 into main Oct 5, 2026
@sayanshaw24
Sayan Shaw (sayanshaw24) deleted the sayanshaw/qmoe-int2-webgpu branch October 5, 2026 22:20

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.

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Copilot review overview

Review effort: Lite
Findings: 1 High severity · 2 Medium severity

Open (3)
Resolved since last review (5)

Comment thread onnxruntime/contrib_ops/webgpu/moe/qmoe.h
Comment thread onnxruntime/contrib_ops/webgpu/moe/qmoe.cc
Comment thread onnxruntime/test/providers/webgpu/contrib_ops/moe_test.cc
kunal-vaishnavi pushed a commit that referenced this pull request Oct 8, 2026
## Summary

Adds direct WebGPU W8A16 support for QMoE expert weights stored as FP8
E4M3 with 128x128 FP32 block scales, matching the Qwen 3.8 Flash Next
MTP expert checkpoint format without requantization or dense weight
materialization.

## Changes

- Extends the existing `quant_type="fp8"` QMoE mode: `block_size=128`
consumes `fc*_scales` shaped `[E, ceil(N/128), ceil(K/128)]`; omitted
block size retains the existing CUDA global-scale FP8 contract.
- Adds a selected-expert WebGPU shader that decodes raw E4M3 bytes and
applies block scales during FP16/FP32 accumulation.
- Supports direct expert dispatch for prefill and indirect top-k expert
dispatch for single-token decode.
- Keeps integer, FP4, NVFP4, and WFP4AFP8 behavior unchanged.
- Adds exact two-expert WebGPU coverage with distinct per-expert scale
products.

## Historical validation (initial Windows run)

- Windows Release `onnxruntime_providers_webgpu` builds successfully.
- Windows Release `onnxruntime_provider_test` builds successfully.
- `MoETest.QMoETest_WebGPU_BlockFp8_128x128` passes locally on D3D12.
- `clang-format --dry-run --Werror` and `git diff --check` pass.
- Editor diagnostics report no errors in changed files.

## Target / follow-up

- Targets `main` directly; the WebGPU MoE/QMoE prerequisite from #32447
is merged. This PR's review diff is limited to six FP8-related files,
independent of the Qwen integration branch.
- GenAI export wiring for MTP block-FP8 QMoE and full-model
chat/performance measurements remain follow-up work. The INT4 RTN model
measurements are not validation of the block-FP8 checkpoint.

## Historical validation (earlier main restack)

- Linux Release static WebGPU provider and provider-test builds passed
on current `main`.
- All 14 `MoETest.QMoETest_WebGPU_*` tests passed on NVIDIA H200/Vulkan,
including single-token decode, a 3x3 non-word-aligned expert-boundary
case, a 129x129 four-scale-tile case, and negative integer-weight
validation.
- Focused `clang-format --dry-run --Werror` and `git diff --check`
passed.


## Historical review follow-up (October 5)

Added a separate-FC3 block-FP8 single-token test covering k=2 indirect
expert routing, broadcast input, SwiGLU, and distinct per-expert
FC1/FC2/FC3 scales. All 17 focused QMoE WebGPU tests passed on
H200/Vulkan. All review threads have replies and are resolved; CI is
rerunning for the new commit.

## Historical main update (October 5)

Merged current `main` after #32878 landed and reconciled block-FP8
routing with its INT2/mixed-width QMoE logic. The review diff remains
limited to the FP8 QMoE files. An isolated Linux static WebGPU
provider/test build and all 14 focused block-FP8 plus INT2 compatibility
tests passed on H200; the stacked tiled follow-up also passed 15 focused
H200 tests. New-head CI is running.

## Latest validation (October 6, `40cebdeeef`)

- Merged upstream `main` at `aac791ef2408`, resolved the QMoE
schema/documentation conflicts, and confirmed GitHub reports this head
as mergeable.
- The Linux static WebGPU `onnxruntime_provider_test` build passed; all
26 focused `MoETest.QMoETest_WebGPU_*` tests passed on NVIDIA
H200/Vulkan. The numerical block-FP8 reference now covers both FP16 and
FP32 inputs/outputs, including FP8 decoding, accumulation, and biases.
- Before FC1, FC2, or optional FC3 dispatch, the WebGPU path rejects
workgroup dimensions that would be normalized and projections exceeding
32-bit shader addressing. Direct expert byte offsets and view sizes use
64-bit arithmetic; the shared limit also covers indirect expert
indexing. The oversized-dispatch rejection has an H200 regression test.
- The six review threads concerning FP32 coverage, dispatch
normalization, and expert-offset overflow have replies and are resolved.
New-head CI is still pending; earlier test counts and CI statements
above describe their respective historical heads, not the current
result.
## October 6 follow-up

Reject orphan block-FP8 FC3 scales instead of silently ignoring them
(09737a3); 27 focused H200 WebGPU QMoE tests pass. Refreshed
generated ContribOperators.md to match the schema and address Windows
GPU documentation CI. Fresh CI is pending.
## October 7 review follow-up

Reject all four optional activation-scale inputs (17–20) in WebGPU
block-FP8 mode instead of silently ignoring them. The GPU-only negative
regression supplies each input separately. Updated the schema and
generated operator docs to state that activation scales are not
accepted. All 28 focused H200 WebGPU QMoE tests pass on eb51a28;
new-head CI is pending.
## October 7 CI follow-up

Merged current main (6030cc6), including upstream CUDA GQA fix
#33179: the previous Linux/Windows CUDA and TensorRT builds failed
because `is_int8` was used before its declaration in upstream CUDA code,
not in this PR's WebGPU changes. The updated Linux static WebGPU
provider test builds and all 28 focused H200 QMoE tests pass.
Previous-head Python 3.14 and Windows QNN jobs failed ONNX
materialized-node tests; Android minimal binary size exceeded its
threshold by 1,494 bytes. New-head CI is running; these failures are not
yet claimed fixed until the fresh checks report.
## October 7 upstream CI fix merged

#33168 merged to main and corrects the ONNX wire-type to C-API-type
conversion behind the old-head Windows QNN/Python 3.14 materialized-node
failures. Merged main at fdf7f9d; static WebGPU tests build and all
28 focused H200 QMoE cases pass. The old-head Android binary-size check
passed. New-head CI is running; no claim of CI green until the fresh
checks finish.

---------

Co-authored-by: Sayan Shaw <sayanshaw@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
kunal-vaishnavi pushed a commit that referenced this pull request Oct 9, 2026
## Summary
- Replace the single-token QMoE gate full expert bubble sort with a
stable parallel TopK reduction for normalized routing. Preserve
descending-logit/index-ascending ties, softmax weight order, and the
existing full sort when routing weights are not normalized (the
denominator then needs all experts).
- Cover boundary ties in 3-, 5-, 6-, and 512-expert pools with nonzero
expert outputs. Skip the 512-expert case when either adapter workgroup
limit is below 512.
- Assert the selected gate indices directly for FP16/FP32
negative-infinity logits (`[0, -inf, -inf]` selects `[0, 1]`) and for
all-equal logits with `k == num_experts` (`[0, 1, 2, 3]`). The
negative-infinity assertion fails with the original comparator (it
selects the invalid sentinel index 3).
- Exercise FP16/FP32 nonnormalized routing with an excluded expert
contributing appreciably to the all-expert softmax denominator.

## Validation
- On head `45037061757c`, merged `origin/main` at `9f6baac7319a`
(including #33168) without rebasing or gate conflicts. Rebuilt both the
static test-all and split provider-test targets; all 28 focused WebGPU
MoE tests passed on H200, including FP16/FP32 gate-index readback.
Checked free GPU memory before the run; fresh CI for this head is in
progress.
- Main-based static WebGPU build and all 22 focused WebGPU QMoE tests
passed on H200 after merging `main` at
`aac791ef2408a266d8ee59dd5fa0ce11d1a5a0bb`, including the new tie,
gate-index, and nonnormalized-routing regressions.
- Qwen3.8-Flash-Next INT4 RTN/INT8-head export, 512 experts, k=10, FP16
I/O, one-token chunks, H200: 16-token prefill 11.64 s -> 0.90 s and
steady decode ~0.674 s -> ~0.045 s/token. Baseline versus optimized
logits were bit-identical (248,320 logits), and the first five generated
token IDs matched. This full-model comparison used a QSA-enabled
experimental build carrying the same single-token gate change because
the export also requires the not-yet-merged SparseAttentionIndexer
kernel; the isolated main-based plugin cannot load that export today.
The PR itself does not depend on the QSA changes.
- This PR changes only the single-token gate. Separate GPU-only
multi-token prefill experiments are intentionally excluded pending
model-quality validation.

## Main updates
- October 5: merged `main` and added the follow-up requested on merged
#32878: two experts use distinct INT2/INT4 packed weights as well as
scales, with matching FP16-aware numerical references. The fixture
explicitly rejects unsupported 8-bit encodings.
- October 6: merged `main` at
`aac791ef2408a266d8ee59dd5fa0ce11d1a5a0bb`; added direct gate-index
assertions, 5-/6-expert pool tests, adapter-limit skip, and the
full-denominator regression. That head was superseded by the fixture
alignment fix below.
- October 6 follow-up: pad the direct gate-index test fixture buffers to
4-byte boundaries for odd FP16 expert counts. On the prior head, macOS
Dawn rejected a six-byte storage buffer and an eight-byte staging copy.
The prior-head Windows job reported a lost device at the same gate-index
test (0 ms), followed by cascading failures; its log does not establish
whether the fixture caused the device loss.
- October 7 follow-up: current-head arm64 Debug reproduced a real
test-fixture readback error: `BufferManager::Download` rounds the copy
to 16 bytes, but the `k=2` indices buffer was only 8 bytes. Align that
buffer to 16 bytes. Windows novcpkg lost the WebGPU device at the same
gate-index test and subsequently failed 1,538 unrelated tests; the
static vcpkg aggregate reported the dependent test failure despite
successful compilation. All 28 focused WebGPU MoE tests pass after the
fix on H200.
- October 7 main refresh: merged `main` at `9f6baac7319a` (including
#33168), rebuilt the split static WebGPU provider-test target, and
passed all 28 focused H200 WebGPU MoE tests on head `45037061757c`.
Prior-head CI failures do not describe this new head; current-head
checks are running.
## October 8 main refresh

Merged `origin/main` at `864d87126864c64651f58a9b51d9006ad71b2c78`
(including #33203) into the gate branch, resolving the test include
conflict by retaining both sets of includes. Updated the direct
gate-buffer test to call upstream `WebGpuDevice(0)` after the context-ID
API change. On head `813f7999e55de5a8d29a7a02dfc869d514f327b4`, the
static WebGPU provider-test build succeeded and all 34 focused
H200/Vulkan `MoETest.QMoETest_WebGPU*` cases passed. Fresh CI is queued;
no new-head CI result is claimed yet.
## Matched synthetic whole-inference benchmark (October 8)

H200/Vulkan ONNX Runtime `perf_test`, synthetic single-token block-FP8
QMoE with 256 experts and top-4 routing, three 100-run trial averages:
merged #32794 baseline **7.09 ms/run** median-trial mean versus the
integrated gate branch **5.56 ms/run** (~**1.28x**). Individual trial
averages ranged **5.71–7.12 ms** for baseline and **5.46–5.89 ms** for
the integrated branch; these overlapping ranges warrant a variance
caveat. This measures whole inference, **not** the isolated gate kernel
or a full checkpoint. The separate combined branch passed **52/52**
focused tests; that result is not claimed as an isolated #33058-head
test run.
## October 8 documentation follow-up

Added [WebGPU QMoE single-token gate
documentation](https://github.com/sayanshaw24/onnxruntime/blob/8d38e0e31be0216e7d3b937578efa113fccee4ca/docs/contrib_ops/webgpu/qmoe_gate.md)
on head `8d38e0e31b`, covering normalized top-k, tie and -inf handling,
nonnormalized fallback, adapter limits, and benchmark scope. The
previous-head Windows documentation-build job stopped while compiling
unrelated CUDA sources before document generation, so its dependent
validation failure does not establish a generated-doc mismatch. The
previous-head Android job failed during NDK setup with a corrupt
downloaded archive. Fresh-head CI is running.

---------

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