KernelBench hard · B200

Sonic MoE Claude Opus 4.8

passdid not score
harnessclaudeagent session1h 9mtotal wall1h 13mcheck2mbenchmark2moutput tokens101,348cost$8.84gpu-lock wait17sgpu-lock held4mregimecompute

Per-shape vs governing ceilingeach shape graded against whichever binds — bf16 compute or HBM bandwidth

32768×4096×1536×128×84.852 ms7.5%1.27 TB/s · 16% of 8.0 TB/s HBM · also 170 TFLOPS (8% of compute)
4096×2048×1024×64×40.174 ms8.8%3.66 TB/s · 46% of 8.0 TB/s HBM · also 197 TFLOPS (9% of compute)
16384×2048×4096×64×83.288 ms7.4%1.14 TB/s · 14% of 8.0 TB/s HBM · also 167 TFLOPS (7% of compute)

compute-bound memory-bound · bar + right column = official fraction of the ceiling (the geomean input)

geomean(7.5% · 8.8% · 7.4%) = 7.9%

Kernel source (redacted)
"""Grouped GEMM + fused SwiGLU up-projection (MoE FFN) for B200 (SM100).

Per expert e:  h_e = silu(x_e @ W_gate[e]) * (x_e @ W_up[e])

x_e is the slice of permuted hidden_states routed to expert e
(rows [offsets[e]:offsets[e+1]]).

Implementation: a variable-length grouped GEMM in Triton with SwiGLU fused into
the epilogue. The two weight matrices are interleaved along the output dimension
(g,u,g,u,...) so a single MMA per K-step produces both the gate and up
projections; tl.split separates them for the SwiGLU activation. This turns the
fused dual-GEMM into one structurally-standard GEMM with N=2*BLOCK_N, which the
Blackwell tcgen05 path pipelines well. An L2 swizzle (GROUP_M) and a fused
schedule-builder kernel keep launch/scheduling overhead minimal.
"""
from __future__ import annotations

import torch
import torch.nn as nn
import triton
import triton.language as tl


# --------------------------------------------------------------------------
# Schedule builder: map each global tile -> (expert, row_start, row_end).
# One fused launch, no host sync.
# --------------------------------------------------------------------------
@triton.jit
def _build_sched_kernel(off_ptr, te_ptr, trs_ptr, tre_ptr,
                        E: tl.constexpr, BLOCK_M: tl.constexpr):
    g = tl.program_id(0)
    cum = 0
    found_e = -1
    rs = 0
    re = 0
    for e in range(0, E):
        s = tl.load(off_ptr + e)
        en = tl.load(off_ptr + e + 1)
        nt = (en - s + BLOCK_M - 1) // BLOCK_M
        local = g - cum
        cond = (found_e < 0) and (g >= cum) and (g < cum + nt)
        found_e = tl.where(cond, e, found_e)
        rs = tl.where(cond, s + local * BLOCK_M, rs)
        re = tl.where(cond, tl.minimum(s + local * BLOCK_M + BLOCK_M, en), re)
        cum += nt
    tl.store(te_ptr + g, found_e)
    tl.store(trs_ptr + g, rs)
    tl.store(tre_ptr + g, re)


def _build_schedule(expert_offsets, BLOCK_M, max_tiles, E):
    dev = expert_offsets.device
    te = torch.empty(max_tiles, dtype=torch.int32, device=dev)
    trs = torch.empty(max_tiles, dtype=torch.int32, device=dev)
    tre = torch.empty(max_tiles, dtype=torch.int32, device=dev)
    off = expert_offsets.to(torch.int32)
    _build_sched_kernel[(max_tiles,)](off, te, trs, tre, E=E, BLOCK_M=BLOCK_M, num_warps=1)
    return te, trs, tre


# --------------------------------------------------------------------------
# Grouped GEMM + fused SwiGLU. Weights interleaved (g,u,g,u,...) along N.
# --------------------------------------------------------------------------
@triton.jit
def _grouped_swiglu_kernel(
    X_ptr, W_ptr, Out_ptr,
    tile_expert_ptr, tile_row_start_ptr, tile_row_end_ptr,
    H,
    stride_xm, stride_xk,
    stride_we, stride_wk, stride_wn,
    stride_om, stride_on,
    NUM_M_TILES: tl.constexpr, NUM_N_TILES: tl.constexpr,
    BLOCK_M: tl.constexpr, BLOCK_N: tl.constexpr, BLOCK_K: tl.constexpr,
    GROUP_M: tl.constexpr,
):
    pid = tl.program_id(0)
    num_pid_in_group = GROUP_M * NUM_N_TILES
    group_id = pid // num_pid_in_group
    first_pid_m = group_id * GROUP_M
    group_size = min(NUM_M_TILES - first_pid_m, GROUP_M)
    pid_m = first_pid_m + (pid % group_size)
    pid_n = (pid % num_pid_in_group) // group_size

    expert = tl.load(tile_expert_ptr + pid_m)
    if expert < 0:
        return

    row_start = tl.load(tile_row_start_ptr + pid_m)
    row_end = tl.load(tile_row_end_ptr + pid_m)

    offs_m = row_start + tl.arange(0, BLOCK_M)
    offs_k = tl.arange(0, BLOCK_K)
    offs_wn = pid_n * (2 * BLOCK_N) + tl.arange(0, 2 * BLOCK_N)
    mask_m = offs_m < row_end

    x_ptrs = X_ptr + (offs_m[:, None] * stride_xm + offs_k[None, :] * stride_xk)
    w_ptrs = W_ptr + expert * stride_we + (offs_k[:, None] * stride_wk + offs_wn[None, :] * stride_wn)

    acc = tl.zeros((BLOCK_M, 2 * BLOCK_N), dtype=tl.float32)
    for k in range(0, H, BLOCK_K):
        x = tl.load(x_ptrs, mask=mask_m[:, None], other=0.0)
        w = tl.load(w_ptrs)
        acc = tl.dot(x, w, acc)
        x_ptrs += BLOCK_K * stride_xk
        w_ptrs += BLOCK_K * stride_wk

    g, u = tl.split(tl.reshape(acc, (BLOCK_M, BLOCK_N, 2)))
    out = (g * tl.sigmoid(g) * u).to(Out_ptr.dtype.element_ty)

    offs_on = pid_n * BLOCK_N + tl.arange(0, BLOCK_N)
    out_ptrs = Out_ptr + (offs_m[:, None] * stride_om + offs_on[None, :] * stride_on)
    tl.store(out_ptrs, out, mask=mask_m[:, None])


# (BLOCK_M, BLOCK_N, BLOCK_K, num_warps, num_stages, GROUP_M)
def _pick_config(H, I, E, T_perm):
    if I >= 4096:                        # intermediate-heavy
        return (256, 128, 64, 8, 3, 8)
    return (256, 128, 64, 8, 3, 4)       # headline / small


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._w_il = None
        self._w_il_ver = None

    def _interleaved_weight(self):
        ver = (self.W_gate._version, self.W_up._version,
               self.W_gate.data_ptr(), self.W_up.data_ptr())
        if self._w_il is None or self._w_il_ver != ver:
            E, H, I = self.E, self.H, self.I
            # (E, H, I, 2) -> (E, H, 2I): columns are g0,u0,g1,u1,...
            self._w_il = torch.stack([self.W_gate, self.W_up], dim=-1).reshape(E, H, 2 * I).contiguous()
            self._w_il_ver = ver
        return self._w_il

    def forward(self, hidden_states: torch.Tensor, expert_offsets: torch.Tensor) -> torch.Tensor:
        T_perm, H = hidden_states.shape
        I, E = self.I, self.E
        out = torch.empty(T_perm, I, dtype=torch.bfloat16, device=hidden_states.device)

        BLOCK_M, BLOCK_N, BLOCK_K, warps, stages, GROUP_M = _pick_config(H, I, E, T_perm)

        W = self._interleaved_weight()
        max_tiles = (T_perm + BLOCK_M - 1) // BLOCK_M + E
        te, trs, tre = _build_schedule(expert_offsets, BLOCK_M, max_tiles, E)

        num_n_tiles = (I + BLOCK_N - 1) // BLOCK_N
        grid = (max_tiles * num_n_tiles,)

        _grouped_swiglu_kernel[grid](
            hidden_states, W, out,
            te, trs, tre,
            H,
            hidden_states.stride(0), hidden_states.stride(1),
            W.stride(0), W.stride(1), W.stride(2),
            out.stride(0), out.stride(1),
            NUM_M_TILES=max_tiles, NUM_N_TILES=num_n_tiles,
            BLOCK_M=BLOCK_M, BLOCK_N=BLOCK_N, BLOCK_K=BLOCK_K,
            GROUP_M=GROUP_M,
            num_warps=warps, num_stages=stages,
        )
        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]

20260620_093607_claude_claude-opus-4-8_06_sonic_moe_swiglu