KernelBench hard · H100
Sonic MoE Grok 4.5
slowdid not score
harnessgrokagent session50mtotal wall53mcheck3mbenchmark—output tokens—gpu-lock wait0sgpu-lock held3mregimecompute
Per-shape vs governing ceilingeach shape graded against whichever binds — bf16 compute or HBM bandwidth
No per-shape benchmark data archived for this run.
Kernel source (redacted)
"""Grouped GEMM + fused SwiGLU for top-K MoE FFN up-projection (H100 / SM90).
Per expert e:
h_e = silu(x_e @ W_gate[e]) * (x_e @ W_up[e])
Triton dual-GEMM kernel with TMA loads:
- variable-length experts scheduled via cached tile maps
- A (hidden) loaded once per K-tile; both gate and up accumulate
- SiLU * mul fused into the epilogue (fp32 accumulate -> bf16 store)
"""
from __future__ import annotations
import math
from typing import Optional
import torch
import torch.nn as nn
import triton
import triton.language as tl
OP_TYPE = "grouped_gemm_swiglu"
SUPPORTED_PRECISIONS = ["bf16"]
HARDWARE_REQUIRED = ["RTX_PRO_6000", "H100", "B200"]
# TMA kernels need a runtime allocator for descriptor scratch.
def _triton_allocator(size: int, alignment: int, stream: int | None):
return torch.empty(size, device="cuda", dtype=torch.int8)
try:
triton.set_allocator(_triton_allocator)
except Exception:
pass
@triton.jit
def _dual_gemm_swiglu_tma_kernel(
x_ptr,
w_gate_ptr,
w_up_ptr,
out_ptr,
expert_offsets_ptr,
tile_expert_ptr,
tile_m_ptr,
tile_n_ptr,
H,
I, # noqa: E741
stride_xm,
stride_we,
stride_wk,
stride_om,
BLOCK_M: tl.constexpr,
BLOCK_N: tl.constexpr,
BLOCK_K: tl.constexpr,
):
pid = tl.program_id(0)
e = tl.load(tile_expert_ptr + pid)
pid_m = tl.load(tile_m_ptr + pid)
pid_n = tl.load(tile_n_ptr + pid)
row_start = tl.load(expert_offsets_ptr + e)
row_end = tl.load(expert_offsets_ptr + e + 1)
n_tokens = row_end - row_start
if n_tokens <= 0:
return
x_base = x_ptr + row_start.to(tl.int64) * stride_xm
out_base = out_ptr + row_start.to(tl.int64) * stride_om
w_gate_base = w_gate_ptr + e.to(tl.int64) * stride_we
w_up_base = w_up_ptr + e.to(tl.int64) * stride_we
a_desc = tl.make_tensor_descriptor(
x_base, shape=[n_tokens, H], strides=[stride_xm, 1],
block_shape=[BLOCK_M, BLOCK_K],
)
bg_desc = tl.make_tensor_descriptor(
w_gate_base, shape=[H, I], strides=[stride_wk, 1],
block_shape=[BLOCK_K, BLOCK_N],
)
bu_desc = tl.make_tensor_descriptor(
w_up_base, shape=[H, I], strides=[stride_wk, 1],
block_shape=[BLOCK_K, BLOCK_N],
)
d_desc = tl.make_tensor_descriptor(
out_base, shape=[n_tokens, I], strides=[stride_om, 1],
block_shape=[BLOCK_M, BLOCK_N],
)
offs_m = pid_m * BLOCK_M
offs_n = pid_n * BLOCK_N
acc_g = tl.zeros((BLOCK_M, BLOCK_N), dtype=tl.float32)
acc_u = tl.zeros((BLOCK_M, BLOCK_N), dtype=tl.float32)
for k in tl.range(0, H, BLOCK_K):
a = a_desc.load([offs_m, k])
bg = bg_desc.load([k, offs_n])
bu = bu_desc.load([k, offs_n])
acc_g = tl.dot(a, bg, acc_g)
acc_u = tl.dot(a, bu, acc_u)
# SiLU(gate) * up
out = (acc_g * tl.sigmoid(acc_g) * acc_u).to(tl.bfloat16)
d_desc.store([offs_m, offs_n], out)
@triton.jit
def _dual_gemm_swiglu_kernel(
x_ptr,
w_gate_ptr,
w_up_ptr,
out_ptr,
expert_offsets_ptr,
tile_expert_ptr,
tile_m_ptr,
tile_n_ptr,
H,
I, # noqa: E741
stride_xm,
stride_xk,
stride_we,
stride_wk,
stride_wn,
stride_om,
stride_on,
BLOCK_M: tl.constexpr,
BLOCK_N: tl.constexpr,
BLOCK_K: tl.constexpr,
):
pid = tl.program_id(0)
e = tl.load(tile_expert_ptr + pid)
pid_m = tl.load(tile_m_ptr + pid)
pid_n = tl.load(tile_n_ptr + pid)
row_start = tl.load(expert_offsets_ptr + e)
row_end = tl.load(expert_offsets_ptr + e + 1)
n_tokens = row_end - row_start
if n_tokens == 0:
return
offs_m = pid_m * BLOCK_M + tl.arange(0, BLOCK_M)
offs_n = pid_n * BLOCK_N + tl.arange(0, BLOCK_N)
offs_k = tl.arange(0, BLOCK_K)
x_base = x_ptr + row_start.to(tl.int64) * stride_xm
w_gate_base = w_gate_ptr + e.to(tl.int64) * stride_we
w_up_base = w_up_ptr + e.to(tl.int64) * stride_we
out_base = out_ptr + row_start.to(tl.int64) * stride_om
acc_g = tl.zeros((BLOCK_M, BLOCK_N), dtype=tl.float32)
acc_u = tl.zeros((BLOCK_M, BLOCK_N), dtype=tl.float32)
mask_m = offs_m < n_tokens
mask_n = offs_n < I
for k in tl.range(0, H, BLOCK_K):
a_ptrs = x_base + (
offs_m[:, None].to(tl.int64) * stride_xm
+ (k + offs_k)[None, :].to(tl.int64) * stride_xk
)
a = tl.load(
a_ptrs,
mask=mask_m[:, None] & ((k + offs_k)[None, :] < H),
other=0.0,
)
b_mask = ((k + offs_k)[:, None] < H) & mask_n[None, :]
bg = tl.load(
w_gate_base
+ (k + offs_k)[:, None].to(tl.int64) * stride_wk
+ offs_n[None, :].to(tl.int64) * stride_wn,
mask=b_mask,
other=0.0,
)
bu = tl.load(
w_up_base
+ (k + offs_k)[:, None].to(tl.int64) * stride_wk
+ offs_n[None, :].to(tl.int64) * stride_wn,
mask=b_mask,
other=0.0,
)
acc_g = tl.dot(a, bg, acc_g)
acc_u = tl.dot(a, bu, acc_u)
out = (acc_g * tl.sigmoid(acc_g) * acc_u).to(tl.bfloat16)
tl.store(
out_base
+ offs_m[:, None].to(tl.int64) * stride_om
+ offs_n[None, :].to(tl.int64) * stride_on,
out,
mask=mask_m[:, None] & mask_n[None, :],
)
# Best configs found by sweep on H100 PCIe (dual TMA).
_CONFIGS = [
(128, 128, 64, 8, 4, True),
(128, 128, 64, 8, 3, True),
(128, 128, 32, 8, 5, True),
(256, 64, 64, 8, 4, True),
(128, 64, 64, 4, 3, True),
(64, 128, 64, 4, 5, True),
(128, 64, 64, 4, 4, True),
(64, 64, 64, 4, 4, True),
(128, 128, 64, 8, 4, False),
(128, 64, 64, 4, 4, False),
]
_best_cfg: dict = {}
_tile_cache: dict = {}
def _build_tile_maps(
expert_offsets: torch.Tensor,
I: int, # noqa: E741
BLOCK_M: int,
BLOCK_N: int,
):
"""Build (expert, m_tile, n_tile) maps. Cached by offsets content."""
# Fast path: if offsets look balanced, build without full bytes hash every time
# using (ptr, last_value, E, BM, BN, I) — invalidated when offsets change.
off_cpu = expert_offsets.detach().to(device="cpu", dtype=torch.int32).contiguous()
key = (off_cpu.numpy().tobytes(), I, BLOCK_M, BLOCK_N, str(expert_offsets.device))
cached = _tile_cache.get(key)
if cached is not None:
return cached
E = off_cpu.numel() - 1
experts, ms, ns = [], [], []
num_n = (I + BLOCK_N - 1) // BLOCK_N
# L2-friendly order: for each expert, iterate n-tiles outer so weight tiles
# stay hot while walking m (A changes, B fixed for a given n-tile).
for e in range(E):
n_tok = int(off_cpu[e + 1]) - int(off_cpu[e])
if n_tok <= 0:
continue
num_m = (n_tok + BLOCK_M - 1) // BLOCK_M
for pid_n in range(num_n):
for pid_m in range(num_m):
experts.append(e)
ms.append(pid_m)
ns.append(pid_n)
device = expert_offsets.device
if not experts:
empty = torch.empty(0, dtype=torch.int32, device=device)
result = (empty, empty, empty, 0)
else:
result = (
torch.tensor(experts, dtype=torch.int32, device=device),
torch.tensor(ms, dtype=torch.int32, device=device),
torch.tensor(ns, dtype=torch.int32, device=device),
len(experts),
)
if len(_tile_cache) > 32:
_tile_cache.clear()
_tile_cache[key] = result
return result
def _can_use_tma(hs, wg, I, BLOCK_M, BLOCK_N, BLOCK_K) -> bool: # noqa: E741
if hs.stride(-1) != 1 or wg.stride(-1) != 1:
return False
if (hs.stride(0) % 8) != 0 or (wg.stride(1) % 8) != 0:
return False
if (I % 8) != 0:
return False
if (BLOCK_K % 8) != 0 or (BLOCK_N % 8) != 0 or (BLOCK_M % 8) != 0:
return False
return True
def grouped_gemm_swiglu(
hidden_states: torch.Tensor,
W_gate: torch.Tensor,
W_up: torch.Tensor,
expert_offsets: torch.Tensor,
cfg: Optional[tuple] = None,
) -> torch.Tensor:
T_perm, H = hidden_states.shape
E, H_w, I = W_gate.shape # noqa: E741
assert H_w == H and W_up.shape == (E, H, I)
if cfg is None:
avg = max(int(T_perm) // max(E, 1), 1)
if avg >= 1024 and I >= 1024:
cfg = (128, 128, 64, 8, 4, True)
elif avg >= 256:
cfg = (128, 64, 64, 4, 3, True)
else:
cfg = (64, 64, 64, 4, 4, True)
BLOCK_M, BLOCK_N, BLOCK_K, num_warps, num_stages, use_tma = cfg
tile_expert, tile_m, tile_n, total_tiles = _build_tile_maps(
expert_offsets, I, BLOCK_M, BLOCK_N
)
out = torch.empty(T_perm, I, dtype=torch.bfloat16, device=hidden_states.device)
if total_tiles == 0:
return out
hs = hidden_states if hidden_states.is_contiguous() else hidden_states.contiguous()
wg = W_gate if W_gate.is_contiguous() else W_gate.contiguous()
wu = W_up if W_up.is_contiguous() else W_up.contiguous()
eo = expert_offsets if expert_offsets.is_contiguous() else expert_offsets.contiguous()
use_tma = bool(use_tma) and _can_use_tma(hs, wg, I, BLOCK_M, BLOCK_N, BLOCK_K)
if use_tma:
_dual_gemm_swiglu_tma_kernel[(total_tiles,)](
hs, wg, wu, out, eo,
tile_expert, tile_m, tile_n,
H, I,
hs.stride(0),
wg.stride(0),
wg.stride(1),
out.stride(0),
BLOCK_M=BLOCK_M,
BLOCK_N=BLOCK_N,
BLOCK_K=BLOCK_K,
num_warps=num_warps,
num_stages=num_stages,
)
else:
_dual_gemm_swiglu_kernel[(total_tiles,)](
hs, wg, wu, out, eo,
tile_expert, tile_m, tile_n,
H, I,
hs.stride(0), hs.stride(1),
wg.stride(0), wg.stride(1), wg.stride(2),
out.stride(0), out.stride(1),
BLOCK_M=BLOCK_M,
BLOCK_N=BLOCK_N,
BLOCK_K=BLOCK_K,
num_warps=num_warps,
num_stages=num_stages,
)
return out
def _autotune_once(hidden_states, W_gate, W_up, expert_offsets) -> tuple:
T_perm, H = hidden_states.shape
E, _, I = W_gate.shape # noqa: E741
avg = max(T_perm // max(E, 1), 1)
bucket = 1 << int(math.floor(math.log2(max(avg, 1))))
key = (H, I, E, bucket)
if key in _best_cfg:
return _best_cfg[key]
ok = []
for c in _CONFIGS:
try:
grouped_gemm_swiglu(hidden_states, W_gate, W_up, expert_offsets, cfg=c)
torch.cuda.synchronize()
ok.append(c)
except Exception:
continue
if not ok:
best = (64, 64, 64, 4, 3, False)
_best_cfg[key] = best
return best
best = ok[0]
best_ms = float("inf")
for c in ok:
try:
start = torch.cuda.Event(enable_timing=True)
end = torch.cuda.Event(enable_timing=True)
torch.cuda.synchronize()
start.record()
for _ in range(10):
grouped_gemm_swiglu(hidden_states, W_gate, W_up, expert_offsets, cfg=c)
end.record()
torch.cuda.synchronize()
ms = start.elapsed_time(end) / 10.0
if ms < best_ms:
best_ms = ms
best = c
except Exception:
continue
_best_cfg[key] = best
return best
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)
self._tuned = False
self._cfg = None
def forward(
self,
hidden_states: torch.Tensor,
expert_offsets: torch.Tensor,
) -> torch.Tensor:
if not self._tuned and hidden_states.is_cuda:
try:
triton.set_allocator(_triton_allocator)
except Exception:
pass
self._cfg = _autotune_once(
hidden_states, self.W_gate, self.W_up, expert_offsets
)
self._tuned = True
return grouped_gemm_swiglu(
hidden_states, self.W_gate, self.W_up, expert_offsets, cfg=self._cfg
)
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]
20260709_045137_grok_grok-4.5_06_sonic_moe_swiglu