Repository navigation
perf(moe): wire olmoe to the fused decode-MoE kernel - #314
Merged
Merged
Conversation
Migrate OLMoE from its local SwitchGLU/SwitchLinear pair to the shared crate::models::switch_layers::SwitchGLU, then add the one-branch fused single-token decode dispatch that already covers qwen3_moe, qwen3_next, dots.llm1, gemma4, qwen2_moe, mixtral, lfm2, qwen3_vl_moe, and phimoe.
What changed in src/models/olmoe.rs:
- Remove local SwitchLinear enum + forward impl (~68 lines) and local SwitchGLU struct + forward/gather_sort/scatter_unsort impls (~117 lines). Neither was imported by any other module.
- Retype SparseMoeBlock.experts from the deleted local SwitchGLU to crate::models::switch_layers::SwitchGLU.
- SparseMoeBlock::from_weights: replace SwitchGLU::from_weights(weights, args, prefix) with crate::models::switch_layers::SwitchGLU::from_weights(weights, prefix, args.group_size(), args.bits()). Prefix passed is {layer}.switch_mlp; standard gate_proj/up_proj/down_proj leaf names match the shared loader directly. The sanitize_weights helper already stacks per-expert weights under that prefix before from_weights runs.
- Remove local impl SwitchGLU { from_weights } and impl SwitchLinear { from_weights } (~40 lines). Helper functions get_weight_copy and sanitize_weights are kept (used by Attention and OlmoeModel::from_weights).
- SparseMoeBlock::forward: replace the bare experts.forward + moe_weighted_sum dispatch with the standard fused-kernel branch (mirror of qwen3_moe). OLMoE's expert intermediate is 1024, below MLXCEL_FUSED_MOE_MAX_DFF (4096), so the kernel dispatches unconditionally at single-token decode and produces a real throughput gain. No fallback note needed.
- Add a narrow compile-time unit test confirming fused_moe_enabled() is callable from olmoe's scope.
docs/benchmark_results/fused-moe-decode-kernel-design.md:
- Bump wired-path count from nine to ten.
- Move olmoe entry from the not-wired list to the wired list with a brief description of checkpoint layout and the fact that the kernel actually fires (intermediate 1024 < DFF cap).
- not-wired list now contains only minimax.
Member
Author
|
Rebased onto main (picks up the olmoe q_norm crash fix #316). Orchestrator validation on |
inureyes
force-pushed
the
perf/issue-302-olmoe-fused-kernel
branch
from
June 16, 2026 14:33
cf6481f to
c979772
Compare
7 tasks
be-free-snowboy
pushed a commit
to freestylejs/snowbrain-mlxcel
that referenced
this pull request
Oct 27, 2026
Migrate OLMoE from its local SwitchGLU/SwitchLinear pair to the shared crate::models::switch_layers::SwitchGLU, then add the one-branch fused single-token decode dispatch that already covers qwen3_moe, qwen3_next, dots.llm1, gemma4, qwen2_moe, mixtral, lfm2, qwen3_vl_moe, and phimoe.
What changed in src/models/olmoe.rs:
- Remove local SwitchLinear enum + forward impl (~68 lines) and local SwitchGLU struct + forward/gather_sort/scatter_unsort impls (~117 lines). Neither was imported by any other module.
- Retype SparseMoeBlock.experts from the deleted local SwitchGLU to crate::models::switch_layers::SwitchGLU.
- SparseMoeBlock::from_weights: replace SwitchGLU::from_weights(weights, args, prefix) with crate::models::switch_layers::SwitchGLU::from_weights(weights, prefix, args.group_size(), args.bits()). Prefix passed is {layer}.switch_mlp; standard gate_proj/up_proj/down_proj leaf names match the shared loader directly. The sanitize_weights helper already stacks per-expert weights under that prefix before from_weights runs.
- Remove local impl SwitchGLU { from_weights } and impl SwitchLinear { from_weights } (~40 lines). Helper functions get_weight_copy and sanitize_weights are kept (used by Attention and OlmoeModel::from_weights).
- SparseMoeBlock::forward: replace the bare experts.forward + moe_weighted_sum dispatch with the standard fused-kernel branch (mirror of qwen3_moe). OLMoE's expert intermediate is 1024, below MLXCEL_FUSED_MOE_MAX_DFF (4096), so the kernel dispatches unconditionally at single-token decode and produces a real throughput gain. No fallback note needed.
- Add a narrow compile-time unit test confirming fused_moe_enabled() is callable from olmoe's scope.
docs/benchmark_results/fused-moe-decode-kernel-design.md:
- Bump wired-path count from nine to ten.
- Move olmoe entry from the not-wired list to the wired list with a brief description of checkpoint layout and the fact that the kernel actually fires (intermediate 1024 < DFF cap).
- not-wired list now contains only minimax.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Migrate OLMoE from its local
SwitchGLU/SwitchLinearpair to the sharedcrate::models::switch_layers::SwitchGLU, then wire the one-branch fused single-token decode dispatch. OLMoE's expert intermediate (1024) is belowMLXCEL_FUSED_MOE_MAX_DFF(4096), so the kernel dispatches on every single-token decode step and delivers a real throughput gain.What changed
src/models/olmoe.rs: removed localSwitchLinearenum (68 lines) and localSwitchGLUstruct withforward/gather_sort/scatter_unsortimpls (117 lines). No other module imported either type.SparseMoeBlock.expertsretyped from the deleted localSwitchGLUtocrate::models::switch_layers::SwitchGLU.SparseMoeBlock::from_weights: construction now callscrate::models::switch_layers::SwitchGLU::from_weights(weights, "{layer}.switch_mlp", group_size, bits). Standardgate_proj/up_proj/down_projleaf names match the shared loader directly; the existingsanitize_weightshelper already stacks per-expert weights under that prefix.impl SwitchGLU { from_weights }andimpl SwitchLinear { from_weights }(40 lines).get_weight_copyandsanitize_weightsare kept (still used byAttentionandOlmoeModel::from_weights).SparseMoeBlock::forward: bareexperts.forward+moe_weighted_sumdispatch replaced with the standard fused-kernel branch (mirror of qwen3_moe/phimoe).fused_moe_enabled()is callable from olmoe's scope.docs/benchmark_results/fused-moe-decode-kernel-design.md: wired-path count bumped from nine to ten; olmoe entry moved from the not-wired list to the wired list; not-wired list now contains only minimax.Test plan
cargo check --lib --tests --features metal,acceleratepasses cleancargo clippy --lib --tests --features metal,accelerate -- -D warningspasses cleanmlx-community/OLMoE-1B-7B-Instruct-4bit) pending the orchestratorCloses #302