KernelBench hard · H100
Sonic MoE MiniMax-M3
7.39%geomean peak fraction across shapes
harnessminimax-claudeagent session45mtotal wall51mcheck3mbenchmark3moutput tokens—gpu-lock wait9sgpu-lock held6mregimecompute
Per-shape vs governing ceilingeach shape graded against whichever binds — bf16 compute or HBM bandwidth
32768×4096×1536×128×816.911 ms6.5%0.37 TB/s · 18% of 2.0 TB/s HBM · also 49 TFLOPS (6% of compute)
4096×2048×1024×64×40.426 ms10.7%1.50 TB/s · 73% of 2.0 TB/s HBM · also 81 TFLOPS (11% of compute)
16384×2048×4096×64×812.370 ms5.9%0.30 TB/s · 15% of 2.0 TB/s HBM · also 44 TFLOPS (6% of compute)
compute-bound memory-bound · bar + right column = official fraction of the ceiling (the geomean input)
geomean(6.5% · 10.7% · 5.9%) = 7.4%
Kernel source (redacted)
"""Up-projection of a top-K MoE FFN: grouped GEMM with fused SwiGLU (Triton, H100).
Per expert e, compute:
h_e = silu(x_e @ W_gate[e]) * (x_e @ W_up[e])
x_e is the slice of permuted hidden states owned by expert e:
rows [expert_offsets[e] : expert_offsets[e+1]] of hidden_states.
Strategy:
* Pack W_gate and W_up along the K dim: W_packed[e] = concat(W_gate[e], W_up[e], dim=0)
-> shape (E, 2*H, I). One tl.dot accumulates the full (gate, up) output at once
over the doubled K dimension, with the K-axis split into two halves in the epilogue.
* 3D launch grid: (n_blocks, m_blocks, E). Each kernel instance handles a
(n_tile, m_tile) sub-tile of one expert's output. If the tile lies past
the expert's last row, the program early-exits.
* Epilogue fuses silu(gate)*up in fp32 and writes bf16.
"""
from __future__ import annotations
import torch
import torch.nn as nn
import triton
import triton.language as tl
@triton.jit
def grouped_swiglu_packed_kernel(
A_ptr, # (T_perm, H) bf16
Wg_ptr, Wu_ptr, # (E, H, I) bf16, two halves of "packed" weight
C_ptr, # (T_perm, I) bf16
expert_offsets_ptr, # (E+1,) int32
H, I,
stride_am, stride_ak,
stride_we, stride_wn, stride_wk,
stride_cm, stride_cn,
BLOCK_M: tl.constexpr,
BLOCK_N: tl.constexpr,
BLOCK_K: tl.constexpr,
SPLIT_K: tl.constexpr, # K-iterations for gate + K-iterations for up; = 2 always
GROUP_M: tl.constexpr,
):
# Super-grouping for L2 reuse (Triton matmul tutorial trick).
pid = tl.program_id(0)
num_pid_m = tl.cdiv(tl.num_programs(1) * tl.num_programs(2), 1) # not used
# We'll compute pid_n and pid_m from a flat pid across (n_blocks * m_blocks)
# with a grouping by m to improve L2 locality.
pid_n = tl.program_id(0)
pid_m = tl.program_id(1)
pid_e = tl.program_id(2)
expert_start = tl.load(expert_offsets_ptr + pid_e)
expert_end = tl.load(expert_offsets_ptr + pid_e + 1)
m_pos = expert_start + pid_m * BLOCK_M
if m_pos >= expert_end:
return
offs_m = m_pos + tl.arange(0, BLOCK_M)
offs_n = pid_n * BLOCK_N + tl.arange(0, BLOCK_N)
m_mask = offs_m < expert_end
a_ptrs = A_ptr + offs_m[:, None] * stride_am
eid = pid_e.to(tl.int64)
wg_ptrs = Wg_ptr + eid * stride_we + offs_n[None, :] * stride_wn
wu_ptrs = Wu_ptr + eid * stride_we + offs_n[None, :] * stride_wn
gate_acc = tl.zeros((BLOCK_M, BLOCK_N), dtype=tl.float32)
up_acc = tl.zeros((BLOCK_M, BLOCK_N), dtype=tl.float32)
for k in range(0, H, BLOCK_K):
offs_k = k + tl.arange(0, BLOCK_K)
a = tl.load(
a_ptrs + offs_k[None, :] * stride_ak,
mask=m_mask[:, None], other=0.0,
)
wg = tl.load(wg_ptrs + offs_k[:, None] * stride_wk)
wu = tl.load(wu_ptrs + offs_k[:, None] * stride_wk)
gate_acc = tl.dot(a, wg, gate_acc)
up_acc = tl.dot(a, wu, up_acc)
silu_gate = gate_acc * tl.sigmoid(gate_acc)
out = silu_gate * up_acc
c_ptrs = C_ptr + offs_m[:, None] * stride_cm + offs_n[None, :] * stride_cn
tl.store(c_ptrs, out.to(tl.bfloat16), mask=m_mask[:, None])
# Block sizes tuned for H100. H and I are multiples of these in all test shapes.
# SMEM budget: A (BLOCK_M*BLOCK_K) + W_gate (BLOCK_K*BLOCK_N) + W_up (BLOCK_K*BLOCK_N)
# per stage * num_stages <= 200KB (H100 limit ~228KB).
_BLOCK_M = 128
_BLOCK_N = 128
_BLOCK_K = 64
_NUM_STAGES = 4
_NUM_WARPS = 8
class Model(nn.Module):
def __init__(self, T_total: int, H: int, I: int, E: int, K: int): # noqa: E741
super().__init__()
self.T_total = T_total
self.H = H
self.I = I
self.E = E
self.K = K
self.W_gate = nn.Parameter(torch.empty(E, H, I, dtype=torch.bfloat16))
self.W_up = nn.Parameter(torch.empty(E, H, I, dtype=torch.bfloat16))
nn.init.normal_(self.W_gate, std=0.02)
nn.init.normal_(self.W_up, std=0.02)
def forward(
self,
hidden_states: torch.Tensor, # (T_perm, H) bf16
expert_offsets: torch.Tensor, # (E+1,) int32
) -> torch.Tensor:
T_perm, H = hidden_states.shape
I = self.I
E = self.E
out = torch.empty(T_perm, I, dtype=torch.bfloat16, device=hidden_states.device)
m_per_expert = (T_perm + E - 1) // E
m_blocks = (m_per_expert + _BLOCK_M - 1) // _BLOCK_M
n_blocks = (I + _BLOCK_N - 1) // _BLOCK_N
grid = (n_blocks, m_blocks, E)
grouped_swiglu_packed_kernel[grid](
hidden_states, self.W_gate, self.W_up, out,
expert_offsets,
H, I,
hidden_states.stride(0), hidden_states.stride(1),
self.W_gate.stride(0), self.W_gate.stride(2), self.W_gate.stride(1),
out.stride(0), out.stride(1),
BLOCK_M=_BLOCK_M, BLOCK_N=_BLOCK_N, BLOCK_K=_BLOCK_K,
SPLIT_K=2,
GROUP_M=8,
num_stages=_NUM_STAGES,
num_warps=_NUM_WARPS,
)
return out
# Module-level shape shims, rewritten by check.py / benchmark.py per shape.
T_total = 32768
H = 4096
I = 1536 # noqa: E741
E = 128
K = 8
def _build_routing(T_total: int, E: int, K: int, device: str = "cpu") -> torch.Tensor:
T_perm = T_total * K
base = T_perm // E
rem = T_perm - base * E
counts = torch.full((E,), base, dtype=torch.int32, device=device)
counts[:rem] += 1
offsets = torch.zeros(E + 1, dtype=torch.int32, device=device)
offsets[1:] = torch.cumsum(counts, dim=0)
return offsets
def get_inputs():
T_perm = T_total * K
hidden_states = torch.randn(T_perm, H, dtype=torch.bfloat16) * 0.1
expert_offsets = _build_routing(T_total, E, K)
return [hidden_states, expert_offsets]
def get_init_inputs():
return [T_total, H, I, E, K]
20260618_065408_minimax-claude_MiniMax-M3_06_sonic_moe_swiglu