Skip to content

perf(moe): fused MoE expert decode kernel, correctness-validated (#268 step 2a) - #275

Merged
inureyes merged 1 commit into
mainfrom
perf/issue-268-fused-moe-expert-kernel
Jun 14, 2026
Merged

inureyes merged 1 commit into
mainfrom
perf/issue-268-fused-moe-expert-kernel

Conversation

@inureyes

Copy link
Copy Markdown
Member

Summary

Step 2a of the fused decode-MoE kernel (#268): a correctness-validated single-launch Metal kernel for the single-token decode expert path. Off by default; the optimization that makes it fast is step 2b.

What it does

One launch computes out[h] = sum_k scores[k] * down_k( silu(gate_k(x)) * up_k(x) )[h] for the K selected experts: in-kernel affine dequant-GEMV of gate/up, swiglu, dequant-GEMV of down, and the score-weighted combine. Built via fast::metal_kernel (same JIT path as ssm_update_kernel).

Multi-bit handling (the non-4-bit point)

The kernel handles power-of-2 bits (4 and 8) via a bits template arg with a clean shift-unpack. For everything else it returns None and the caller uses the proven SwitchGLU + moe_weighted_sum path: 6-bit (dots.llm1's down_proj), non-affine, mismatched bits/group_size across gate/up/down, non-single-token, or threadgroup memory over the 32 KiB Metal limit. So no config ever loses correctness; 6-bit just isn't accelerated yet (MLX packs non-power-of-2 bits differently; that unpack is a 2b/follow-up item).

Validation

Wired into qwen3_moe behind MLXCEL_FUSED_MOE (default off, single-token only). On qwen3-30b-a3b (uniform 4-bit) the kernel output is byte-identical to the reference for both short and 40-token greedy generations.

Why it's off by default

Correctness-first: one threadgroup per token, so a single token has no parallelism to fill the GPU. It runs ~100x slower than MLX's multi-threadgroup gather_qmm (0.46 vs 47 tok/s). Step 2b parallelizes it (multi-threadgroup tiling + SIMD reduction). The correctness (the hard part for a quantized fused kernel) is now proven, so 2b is pure optimization against a fixed reference.

Scope

Issue #268 stays open (2b is the perf win). No default behavior change; the kernel only runs under the env flag. Builds on the design in docs/benchmark_results/fused-moe-decode-kernel-design.md.

…step 2a)

Adds a single-launch Metal kernel for the single-token decode expert path: for the K selected experts, in-kernel affine dequant-GEMV of gate/up, swiglu, dequant-GEMV of down, and the score-weighted combine, all in one launch. Built via fast::metal_kernel (the same JIT path as ssm_update_kernel). Handles power-of-2 bits (4/8); callers fall back to the SwitchGLU + moe_weighted_sum path for 6-bit, non-affine, mismatched bits/group_size across gate/up/down, non-single-token, or oversized threadgroup memory, so dots.llm1's 6-bit experts and every other config stay on the proven path.

Wired into qwen3_moe behind MLXCEL_FUSED_MOE (default off; single-token only), plus the integration point on the shared SwitchGLU for the dots.llm1/nemotron path. On qwen3-30b-a3b the kernel output is byte-identical to the reference for both short and 40-token greedy generations, validating the dequant/GEMV/swiglu/combine math.

Correctness-first: one threadgroup per token, so it is ~100x slower than MLX's multi-threadgroup gather_qmm (0.46 vs 47 tok/s) and stays off by default. Step 2b parallelizes it (multi-threadgroup tiling + SIMD reduction) to turn the validated correctness into the decode win; the design and validation gate are in docs/benchmark_results/fused-moe-decode-kernel-design.md.

No default behavior change.
@inureyes inureyes added type:performance Performance improvements area:core mlxcel-core: MLX FFI, primitives, KV cache, layers area:models Model architectures, weights, loading, metadata status:done Completed labels Jun 14, 2026
@inureyes
inureyes merged commit 7fd4dca into main Jun 14, 2026
5 checks passed
@inureyes
inureyes deleted the perf/issue-268-fused-moe-expert-kernel branch June 14, 2026 02:21
@inureyes inureyes added this to the 0.3 milestone Jun 21, 2026
@inureyes inureyes self-assigned this Aug 31, 2026
be-free-snowboy pushed a commit to freestylejs/snowbrain-mlxcel that referenced this pull request Oct 27, 2026
…lup#268 step 2a) (lablup#275)

Adds a single-launch Metal kernel for the single-token decode expert path: for the K selected experts, in-kernel affine dequant-GEMV of gate/up, swiglu, dequant-GEMV of down, and the score-weighted combine, all in one launch. Built via fast::metal_kernel (the same JIT path as ssm_update_kernel). Handles power-of-2 bits (4/8); callers fall back to the SwitchGLU + moe_weighted_sum path for 6-bit, non-affine, mismatched bits/group_size across gate/up/down, non-single-token, or oversized threadgroup memory, so dots.llm1's 6-bit experts and every other config stay on the proven path.

Wired into qwen3_moe behind MLXCEL_FUSED_MOE (default off; single-token only), plus the integration point on the shared SwitchGLU for the dots.llm1/nemotron path. On qwen3-30b-a3b the kernel output is byte-identical to the reference for both short and 40-token greedy generations, validating the dequant/GEMV/swiglu/combine math.

Correctness-first: one threadgroup per token, so it is ~100x slower than MLX's multi-threadgroup gather_qmm (0.46 vs 47 tok/s) and stays off by default. Step 2b parallelizes it (multi-threadgroup tiling + SIMD reduction) to turn the validated correctness into the decode win; the design and validation gate are in docs/benchmark_results/fused-moe-decode-kernel-design.md.

No default behavior change.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:core mlxcel-core: MLX FFI, primitives, KV cache, layers area:models Model architectures, weights, loading, metadata status:done Completed type:performance Performance improvements

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant