Skip to content

Gemma 4 MoE: re-enable fused com.microsoft::MoE op - #326

Merged
justinchuby merged 1 commit into
mainfrom
gemma4-reenable-fused-moe
Jun 1, 2026
Merged

Gemma 4 MoE: re-enable fused com.microsoft::MoE op#326
justinchuby merged 1 commit into
mainfrom
gemma4-reenable-fused-moe

Conversation

@justinchuby

Copy link
Copy Markdown
Member

Re-enables the fused com.microsoft::MoE op for Gemma 4 now that ORT main supports standard SwiGLU. Replaces the static-unroll fallback that was shipped in #324 as a workaround.

Why now

microsoft/onnxruntime#28467 (QMoE CUDA EP + MoE GEMM Refactor, merged on main) plumbs the existing schema attributes (swiglu_fusion, activation_alpha, activation_beta, swiglu_limit) all the way through to the kernel. Before that PR, the CUDA MoE kernel hardcoded the GPT-OSS values (alpha=1.702, beta=1.0, limit=7.0, interleaved) and silently produced wrong output for any model using standard SwiGLU. That was the root cause tracked in microsoft/onnxruntime-genai#2062.

Attribute set

activation_type           = 'swiglu'
activation_alpha          = 1.0     # no GPT-OSS 1.702 multiplier
activation_beta           = 0.0     # no GPT-OSS '+1' bias on the up branch
swiglu_limit              = inf     # no clipping
swiglu_fusion             = 1       # interleaved
normalize_routing_weights = 1
k                         = top_k

Weight layout

HuggingFace stores experts.gate_up_proj chunked as [E, 2*inter, H] (first inter rows are gate, next inter are up). The CPU MoE kernel still only accepts interleaved layout (contrib_ops/cpu/moe/moe_cpu.cc:27); the new CUDA kernel accepts either. We emit swiglu_fusion=1 (interleaved) for maximum portability and reshape at graph-emit time via Reshape → Transpose → Reshape on the initializer — ORT folds the chain to a single static tensor at session load.

Fallback

_dispatch_moe_fallback (static per-expert unroll) is kept verbatim for EPs that don't advertise supports_fused_moe. The fused path is only taken when ep_capabilities().supports_fused_moe is true (default for the CUDA / DML / default EPs).

Validation (H200, ORT 1.27.0.dev20260511001 which contains #28467)

Check Result
15 gemma4 graph-construction tests all pass
fp16 build of google/gemma-4-26b-a4b-it 30 MoE nodes emitted, all attributes correctly set
InferenceSession load on CUDA EP 12.3s (vs 959s with the previous unrolled fallback — ~78× faster session creation)
Prefill (B=1, S=4) 0.65s, logits well-behaved (no NaN/Inf, top-k IDs in valid vocab range)
lintrunner clean

Compatibility

ORT main now plumbs the SwiGLU schema attributes
(`swiglu_fusion`, `activation_alpha`, `activation_beta`,
`swiglu_limit`) through to the kernel via microsoft/onnxruntime#28467
(QMoE CUDA EP + MoE GEMM Refactor), so the fused MoE op now correctly
implements standard SwiGLU (`y = silu(gate) * up`) rather than only
GPT-OSS-style SwiGLU. This was the original blocker tracked in
microsoft/onnxruntime-genai#2062.

Switch Gemma 4's MoE block back to `com.microsoft::MoE` when the EP
advertises `supports_fused_moe`, with the explicit attribute set
required by standard SwiGLU:

  activation_type     = 'swiglu'
  activation_alpha    = 1.0     (no GPT-OSS 1.702 multiplier)
  activation_beta     = 0.0     (no GPT-OSS "+1" bias on the up branch)
  swiglu_limit        = inf     (no clipping)
  swiglu_fusion       = 1       (interleaved)
  normalize_routing_weights = 1
  k = top_k

The CPU MoE kernel still only supports interleaved layout
(`contrib_ops/cpu/moe/moe_cpu.cc:27`), and the new CUDA kernel
accepts either, so emit interleaved (`swiglu_fusion=1`) for maximum
portability. HuggingFace stores `experts.gate_up_proj` chunked as
`[E, 2*inter, H]` (first `inter` rows = gate, next `inter` = up).
Convert at graph-emit time via Reshape→Transpose→Reshape on the
initializer; ORT folds the chain to a single static tensor at session
load.

The static-unroll `_dispatch_moe_fallback` is kept verbatim for EPs
that don't expose the fused op.

Validation on H200 with ORT 1.27.0.dev20260511001 (which contains
#28467):

  * fp16 build of google/gemma-4-26b-a4b-it: 30 MoE nodes emitted, all
    with the expected attribute set.
  * `InferenceSession` on CUDAExecutionProvider loads in 12.3s
    (vs 959s with the previous fully-unrolled fallback, a ~78x speedup
    on session creation alone).
  * Prefill (B=1, S=4) runs in 0.65s; logits are well-behaved (no NaN
    or Inf, top-k token IDs land in the valid Gemma 4 vocab range).

All 15 `gemma4` graph-construction tests pass, lintrunner clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Justin Chu <justinchu@microsoft.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.

Pull request overview

Re-enables the fused com.microsoft::MoE op for the Gemma 4 MoE block (gated by ep_capabilities().supports_fused_moe), reverting the previous always-fallback workaround now that ORT main (post microsoft/onnxruntime#28467) supports standard SwiGLU semantics. The static-unroll path is retained for EPs without fused MoE support.

Changes:

  • Cache moe_intermediate_size and hidden_size on the block for reshape constants.
  • Emit com.microsoft::MoE with standard-SwiGLU attributes (alpha=1.0, beta=0.0, swiglu_limit=inf, swiglu_fusion=1) and a constant-foldable Reshape→Transpose→Reshape to interleave fc1_experts_weights.
  • Wrap the fused op in CastLike to restore input dtype and keep the legacy fallback under else.

@github-actions

github-actions Bot commented Jun 1, 2026

Copy link
Copy Markdown

🏗️ Architecture Diff

Comparing 395f02258edb88

Model Sub-model Changes Status
gemma4 (gemma4) decoder 0
gemma4 (gemma4) embedding 0
gemma4 (gemma4) vision_encoder 0
gemma4_text model 0

No architecture changes detected.


Legend: ⚪ No change · 🔵 Minor (attrs/inits) · 🟡 Moderate (nodes added/removed) · 🔴 Major (interface changed)

@github-actions

github-actions Bot commented Jun 1, 2026

Copy link
Copy Markdown

Performance Comparison

Comparing 395f02258edb88

Model Metric Baseline Current Delta
bert (feature-extraction) model_size_bytes 359 KB 359 KB +0.0%
bert (feature-extraction) num_nodes 60 60 +0.0%
falcon model_size_bytes 364 KB 364 KB +0.0%
falcon num_nodes 66 66 +0.0%
gemma2 model_size_bytes 428 KB 428 KB +0.0%
gemma2 num_nodes 107 107 +0.0%
gpt2 model_size_bytes 388 KB 388 KB +0.0%
gpt2 num_nodes 53 53 +0.0%
llama model_size_bytes 425 KB 425 KB +0.0%
llama num_nodes 61 61 +0.0%
llama (static-cache) model_size_bytes 425 KB 425 KB +0.0%
llama (static-cache) num_nodes 58 58 +0.0%
mamba (ssm-text-generation) model_size_bytes 296 KB 296 KB +0.0%
mamba (ssm-text-generation) num_nodes 98 98 +0.0%
phi3 model_size_bytes 421 KB 421 KB +0.0%
phi3 num_nodes 59 59 +0.0%
phi3 (static-cache) model_size_bytes 421 KB 421 KB +0.0%
phi3 (static-cache) num_nodes 56 56 +0.0%
qwen2 model_size_bytes 425 KB 425 KB +0.0%
qwen2 num_nodes 61 61 +0.0%
qwen2 (static-cache) model_size_bytes 425 KB 425 KB +0.0%
qwen2 (static-cache) num_nodes 58 58 +0.0%
qwen3_5_moe (hybrid-text-generation) model_size_bytes 506 KB 506 KB +0.0%
qwen3_5_moe (hybrid-text-generation) num_nodes 275 275 +0.0%
qwen3_5_text (hybrid-text-generation) model_size_bytes 458 KB 458 KB +0.0%
qwen3_5_text (hybrid-text-generation) num_nodes 129 129 +0.0%
qwen3_5_vl (hybrid-qwen-vl) model_size_bytes 977 KB 977 KB +0.0%
qwen3_5_vl (hybrid-qwen-vl) num_nodes 413 413 +0.0%
t5 (seq2seq) model_size_bytes 836 KB 836 KB +0.0%
t5 (seq2seq) num_nodes 166 166 +0.0%
whisper (speech-to-text) model_size_bytes 1008 KB 1008 KB +0.0%
whisper (speech-to-text) num_nodes 128 128 +0.0%

No performance regressions.

@codecov

codecov Bot commented Jun 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 80.00000% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/mobius/models/gemma4.py 80.00% 1 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@justinchuby
justinchuby merged commit d84e147 into main Jun 1, 2026
21 of 24 checks passed
@justinchuby
justinchuby deleted the gemma4-reenable-fused-moe branch June 1, 2026 20:27
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.

2 participants