Skip to content

perf(moe): route granite-4.0-h single-token MoE through the fused kernel - #2283

Merged
inureyes merged 3 commits into
mainfrom
fix/issue-2245-granite-fused-moe
Oct 13, 2026
Merged

inureyes merged 3 commits into
mainfrom
fix/issue-2245-granite-fused-moe

Conversation

@inureyes

Copy link
Copy Markdown
Member

Summary

GraniteMoeHybridMoE::forward always ran SwitchGLU::forward plus moe_weighted_sum (the gather_qmm path), so the fused single-token decode MoE kernel (#268, ROCm port #2065) never ran for granite-4.0-h; the #2061 profile measured the fallback MoE at 25.4% of decode GPU time on gfx1151.

What changed

  • At one routed token with MLXCEL_FUSED_MOE on, GraniteMoeHybridMoE::forward calls SwitchGLU::forward_fused_kernel with the router top-k indices and f32 softmax scores and reshapes the result to [1, hidden_dim]; on None or more than one token the gather path is kept.

  • The shared expert and router are unchanged; forward_fused_kernel declines unsupported configs itself, so no extra gating.

  • The Used by list on forward_fused_kernel and the reach statement in the gfx1151 decode profile doc are updated.

  • A reproduction test drives the production forward at one token and asserts its output is bit-exact the fused-kernel computation and not the gather computation; it fails without the routing and passes with it (verified by forcing the fused branch off on gfx1151).

Test plan

  • cargo test --profile test-fast --features rocm granitemoehybrid -- --test-threads=1: 12 passed, 0 failed, including the new routing test and the fused-kernel parity test.

  • logit_trace of granite-4.0-h-tiny-4bit with MLXCEL_FUSED_MOE=0 vs default: 0 of 96 positions mismatched, 0 decided mismatches at gap 2.0.

  • Guarded decode throughput on gfx1151 (three interleaved runs per arm): fused 105.0/105.7/105.2 tok/s vs fallback 95.9/96.2/95.3 tok/s (1211 to 1220 ms vs 1331 to 1344 ms per 128 generated tokens), about 10% faster and well beyond run-to-run spread, so the default stays on.

  • make verify-rocm passes locally; hosted CI is down and the local gate is the merge gate. The branch also carries a mechanical expect_err fix for the clippy 1.97 err_expect lint that has been failing verify-clippy-rocm on main since fc45284.

Closes #2245

GraniteMoeHybridMoE::forward always called SwitchGLU::forward plus moe_weighted_sum, the gather_qmm path, so the fused single-token decode MoE kernel (#268, ROCm port #2065) never ran for granite-4.0-h and every decode step paid the fallback (25.4% of decode GPU time on gfx1151 per the #2061 profile).

Follow the Mixtral pattern: at one token with MLXCEL_FUSED_MOE on, call SwitchGLU::forward_fused_kernel with the router's top-k indices and f32 softmax scores (which already equal renormalized top-k softmax), and reshape the result to [1, hidden_dim]; on None or more than one token keep the gather path. The shared expert and router are unchanged; forward_fused_kernel already declines unsupported configs.

Also expose build_feed_forward/FeedForward/GraniteMoeHybridMoE.switch_mlp at pub(crate) so the granite test module can load a real quantized block, add a Used by list on forward_fused_kernel, and update the #2065 reach statement in the gfx1151 decode profile doc.

Validated on gfx1151: the new granitemoehybrid fused-kernel parity test passes, and logit_trace of granite-4.0-h-tiny-4bit with MLXCEL_FUSED_MOE=0 vs default agrees at 0 of 96 positions (0 decided mismatches at gap 2.0).

Refs #2245
The fused-kernel parity test added with the routing fix calls SwitchGLU::forward_fused_kernel directly, so it passes without the fix too: the method pre-existed and the routing in GraniteMoeHybridMoE::forward is what issue #2245 changed. Add a reproduction test that drives the production forward at one token and asserts its output is bit-exact the fused-kernel computation and not the gather computation, with both candidates built from the block own router outputs; without the routing it fails at the bit-exact assert, verified by forcing the fused branch off on gfx1151.

Also expose GraniteMoeHybridMoE.router and ::forward and GraniteMoeHybridMLP at pub(crate) for the test module; the FeedForward exposure from the routing commit made pub(crate) FeedForward::Dense reach the private GraniteMoeHybridMLP, which cargo build flagged as private_interfaces.

Refs #2245
Clippy 1.97.0 denies err().expect() under the workspace -D warnings gate, so verify-clippy-rocm failed on main since fc45284 shipped the yaml parser replacement; the three test call sites move to expect_err, a mechanical rewrite with no behavior change. Carried on this branch because the local gate is the merge gate while hosted CI is down.
@inureyes
inureyes merged commit a1c5826 into main Oct 13, 2026
27 checks passed
@inureyes
inureyes deleted the fix/issue-2245-granite-fused-moe branch October 13, 2026 01:58
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.

perf(moe): route granite-4.0-h single-token MoE through the fused kernel

1 participant