flashinfer.fused_moe.cute_dsl_fused_moe_bf16¶
- flashinfer.fused_moe.cute_dsl_fused_moe_bf16(x: Tensor, token_selected_experts: Tensor, token_final_scales: Tensor, w1_weight: Tensor, w2_weight: Tensor, num_experts: int, top_k: int, num_local_experts: int | None = None, local_expert_offset: int = 0, use_fused_finalize: bool = True, moe_output: Tensor | None = None, enable_pdl: bool = True, *, intermediate_buffer: Tensor | None = None, tile_size: int | None = None, gemm1_tile_n: int | None = None, gemm2_tile_n: int | None = None, gemm2_tile_k: int | None = None, gemm2_cluster_shape_mn: Tuple[int, int] | None = None, gemm2_raster_along_m: bool | None = None) Tensor¶
SM90 CuTe-DSL fused MoE forward (BF16/FP16, unquantized).
out[t] = sum_k scale[t,k] * ffn_expert(x[t]; e[t,k])withffn(x; e) = (silu(x @ w1_gate[e].T) * (x @ w1_up[e].T)) @ w2_weight[e].T.- Supported configuration:
Arch: SM90 (Hopper) only.
Dtypes: bf16 or fp16 activations and weights (must match), fp32 accumulation; output dtype = input dtype. No quantized paths.
Activation: SwiGLU (SiLU-gated) only, fused into GEMM1.
Routing: pre-routed contract only — the caller runs the router and passes global expert ids plus normalized scales.
top_kis a compile-time constant of the kernels.Parallelism: TP by weight shapes; EP via
num_local_experts+local_expert_offset(tokens routed entirely outside the local shard contribute zeros).Shapes:
hidden % 64 == 0and (GEMM1 reduction) no tile-32 fallback,2I % 64 == 0,I % 32 == 0(weight interleave and GEMM2 tile-k 32 fallback forI % 64 != 0);num_tokens == 0is supported.Execution: CUDA-graph capturable; PDL on by default; fused finalize (default) is atomic and not bitwise-reproducible —
use_fused_finalize=Falseselects the deterministic path.
Tile selection goes through the FlashInfer AutoTuner. Under the
autotune()context every enumeratedSm90MoeTactic(capped at the top-2 legal N tiles per GEMM) is profiled and the per-bucket winner is cached; outside it the cached winner (or the heuristic auto-selection, as the default tactic) dispatches. Explicit tile / cluster / raster / buffer keyword overrides bypass the tuner:with autotune(True): output = cute_dsl_fused_moe_bf16(...)
- Parameters:
x –
[num_tokens, hidden]bf16/fp16.token_selected_experts –
[num_tokens, top_k]int32.token_final_scales –
[num_tokens, top_k]float32, normalized by the caller.w1_weight –
[num_local_experts, 2I, hidden]— up/gate interleaved at 32 columns. Callers may cache this repack; the in-tree reference isinterleave_up_gate_sm90().w2_weight –
[num_local_experts, hidden, I].num_experts – Total (global) expert count.
top_k – Experts per token.
num_local_experts – Experts held by this rank (EP shard); defaults to
num_experts.local_expert_offset – Global id of this shard’s first expert.
use_fused_finalize – True (default) fuses the router-scaled scatter-reduce into GEMM2. The top-k combine then accumulates in the output dtype (
cp.reduce.async.bulk.add): one output-dtype rounding per route on top of the bf16/fp16 intermediate hand-off, and not bitwise-reproducible across runs. False uses the deterministic two-stage path: GEMM2 scatters unscaled rows in expanded (token, slot) order, thenmoe_unpermuteapplies the scales and combines in float32 in a fixed order — one final rounding, at the cost of an extra kernel and the expanded intermediate.moe_output – Optional pre-allocated
[num_tokens, hidden]output (contents overwritten; zeroed internally for the fused finalize).enable_pdl – True (default) launches both GEMMs (and the deterministic path’s
moe_unpermute) with Programmatic Dependent Launch so each kernel’s prologue overlaps its predecessor’s tail. Numerics are unaffected. Part of the kernel compile cache key.intermediate_buffer – Optional pre-allocated GEMM1 output buffer (advanced, keyword-only; bypasses the tuner like the tile overrides).
tile_size – Tile size shared by moe_sort and both GEMMs (64 or 128; keyword-only). Default None auto-selects: 64 below an average of 64 rows per local expert (
num_tokens * top_k / num_local_experts) — small/decode batches pad each expert to a full M-tile, so the smaller tile roughly halves the wasted MMA work; 128 from one full tile per expert up (fatter tiles amortize per-tile fixed costs and B loads). Tiny reductions (per-rankI < 192) switch to 128 already at 16 rows per expert.gemm1_tile_n – N tile for GEMM1 (None auto-selects; keyword-only).
gemm2_tile_n – N tile for GEMM2 (None auto-selects; keyword-only).
gemm2_tile_k – K tile for GEMM2, 64 or 32 (None auto-selects via
_default_gemm2_tile_k(): 32 when 64 does not divide the per-rank I, and on prefill tiles with I >= 384 where its doubled pipeline depth wins; 64 otherwise. Keyword-only).gemm2_cluster_shape_mn – GEMM2 CTA cluster shape,
(1, 1)or(1, 2)(None applies the fallback heuristic; keyword-only).(1, 2)requires an even GEMM2 N-tile count.gemm2_raster_along_m – GEMM2 tile raster order (None auto-selects; keyword-only). M-major confines the finalize scatter-RMW working set to one L2-resident output column band per CTA wave, at the cost of re-reading each A tile once per N tile — the default enables it only where that trade wins: prefill tiles with a large output working set (>= 32 MiB for per-rank
I <= 192, >= 64 MiB above) and per-rankI <= 384.
- Returns:
[num_tokens, hidden]in x’s dtype.