KernelBench hard · B200
Sonic MoE Claude Fable 5
9.19%geomean peak fraction across shapes
manually audited: clean
harnessor-fableagent session2h 41mtotal wall2h 47mcheck3mbenchmark2moutput tokens—gpu-lock wait83sgpu-lock held1h 3mregimecompute
Per-shape vs governing ceilingeach shape graded against whichever binds — bf16 compute or HBM bandwidth
32768×4096×1536×128×84.765 ms7.7%1.30 TB/s · 16% of 8.0 TB/s HBM · also 173 TFLOPS (8% of compute)
4096×2048×1024×64×40.132 ms11.6%4.84 TB/s · 60% of 8.0 TB/s HBM · also 261 TFLOPS (12% of compute)
16384×2048×4096×64×82.805 ms8.7%1.34 TB/s · 17% of 8.0 TB/s HBM · also 196 TFLOPS (9% of compute)
compute-bound memory-bound · bar + right column = official fraction of the ceiling (the geomean input)
geomean(7.7% · 11.6% · 8.7%) = 9.2%
Kernel source (redacted)
"""Grouped GEMM + fused SwiGLU for the MoE up-projection on B200 (SM100).
Primary path — CuTe DSL (CUTLASS 4.6) persistent warp-specialized kernel with
2-CTA tcgen05 MMA, structure adapted from the BSD-3-licensed CUTLASS examples
(dense_gemm_persistent.py / moe grouped examples):
- The gate and up weights are packed host-side (once, cached) into a single
(E, H, 2I) tensor whose columns alternate 128-wide [gate | up] blocks, so
each 256-wide MMA-N tile holds the gate AND up projections of the SAME
128 output columns. One tcgen05 MMA (mma tile 256x256, the full-rate
SM100 bf16 shape; measured 1716 vs 1356 TFLOPS for N=256 vs N=128 on the
stock dense example) accumulates both projections; TMEM holds 2 full
accumulator stages (2 x 256 cols = 512, exactly TMEM capacity).
- Warps 0-3 (epilogue): load the congruent gate/up halves of the
accumulator as two TMEM subtile fragments, compute silu(g) * u in fp32
registers (sigmoid via one tanh.approx SFU op), convert to bf16 and store
directly to global memory with row predication at expert boundaries.
Output addresses come from a stride-0 "fake" packed-N view of C
((128, 2, tiles):(1, 0, 128)) so gate/up halves alias the same output
columns; only the gate half is stored. No TMA store -> no per-expert
tensormaps needed.
- Warp 4 (MMA), Warp 5 (TMA): classic Blackwell producer/consumer through
an mbarrier pipeline; A is addressed with a single global TMA descriptor
plus per-tile dynamic row offsets (cute.domain_offset) — TMA zero-fills
rows past T_perm, and rows past an expert's end are computed but never
stored. B is a plain (2I, H, E) tensor indexed by its L=expert mode.
- Each role runs its own monotonic O(1)-amortized scan of the grouped tile
space (expert-major, short-side-first within an expert), derived on the
fly from expert_offsets: no host sync, no atomics, empty experts skipped.
The (T_perm, 2I) intermediate of an unfused grouped-GEMM + activation pass
never exists: SwiGLU is fused into the epilogue, and the A operand is read
once for both projections.
Fallback path — a persistent Triton kernel (TMA tensor descriptors + warp
specialization) using the same packed-weight trick, plus a fully masked
Triton variant for arbitrary shapes.
"""
from __future__ import annotations
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"]
# ===========================================================================
# CuTe DSL path (primary)
# ===========================================================================
_CUTE_OK = False
try:
from typing import Optional, Tuple, Type, Union
import cuda.bindings.driver as cuda
import cutlass
import cutlass.cute as cute
import cutlass.utils as utils
import cutlass.pipeline as pipeline
from cutlass.pipeline import pipeline_init_arrive, pipeline_init_wait
from cutlass.cute.nvgpu import cpasync, tcgen05
from cutlass.cute.nvgpu.common import CacheEvictionPriority
from cutlass.utils.blackwell_helpers import get_tmem_load_op
from cutlass.utils.gemm.sm100 import transform_partitioned_tensor_layout
from cutlass.cutlass_dsl import Int32, Boolean, dsl_user_op
_CUTE_OK = True
except Exception: # pragma: no cover - fallback to Triton path
_CUTE_OK = False
if _CUTE_OK:
@dsl_user_op
def _domain_offset_aligned(
coord, tensor: cute.Tensor, *, loc=None, ip=None
) -> cute.Tensor:
"""domain_offset that PRESERVES the pointer's assumed alignment.
Our per-tile C offsets are always 128B-aligned (row stride I is a
multiple of 128 elements on the deck shapes; n0 is a multiple of the
MMA tile N). The default cute.domain_offset drops alignment info for
dynamic offsets, which forces scalar predicated stores; this keeps it.
"""
new_ptr = cute.make_ptr(
tensor.element_type,
(tensor.iterator + cute.crd2idx(coord, tensor.layout, loc=loc, ip=ip)).toint(),
tensor.memspace,
assumed_align=tensor.iterator.alignment,
)
return cute.make_tensor(new_ptr, tensor.layout)
class GroupedGemmSwiGLUKernel:
def __init__(
self,
use_2cta_instrs: bool = True,
mma_tiler_mn: Tuple[int, int] = (256, 128),
cluster_shape_mn: Tuple[int, int] = (2, 1),
epi_tile_n: int = 64,
num_acc_stage: int = 2,
raster: int = 0, # 0=short-side-first, 1=m-fastest, 2=n-fastest, 3=grouped
raster_group: int = 6, # chunk size of the longer dim for raster=3
):
self.acc_dtype = cutlass.Float32
self.use_2cta_instrs = use_2cta_instrs
self.cluster_shape_mn = cluster_shape_mn
self.mma_tiler_mn = mma_tiler_mn
self.mma_tiler = (*mma_tiler_mn, 1)
self.epi_tile_n = epi_tile_n
self.arch = "sm_100"
self.cta_group = (
tcgen05.CtaGroup.TWO if use_2cta_instrs else tcgen05.CtaGroup.ONE
)
self.occupancy = 1
self.epilogue_warp_id = (0, 1, 2, 3)
self.mma_warp_id = 4
self.tma_warp_id = 5
self.threads_per_cta = 32 * len(
(self.mma_warp_id, self.tma_warp_id, *self.epilogue_warp_id)
)
self.epilog_sync_bar_id = 1
self.tmem_alloc_sync_bar_id = 2
self.tmem_dealloc_sync_bar_id = 3
# Accumulator stages; num_acc_stage * mma_N TMEM cols must be <= 512.
self.num_acc_stage = num_acc_stage
self.raster = raster
self.raster_group = raster_group
def _create_tiled_mma(self):
return utils.sm100.make_trivial_tiled_mma(
self.a_dtype,
self.a_major_mode,
self.b_major_mode,
self.acc_dtype,
self.cta_group,
self.mma_tiler[:2],
)
def _setup_attributes(self):
tiled_mma = self._create_tiled_mma()
mma_inst_shape_k = cute.size(tiled_mma.shape_mnk, mode=[2])
mma_inst_tile_k = 4
self.mma_tiler = (
self.mma_tiler[0],
self.mma_tiler[1],
mma_inst_shape_k * mma_inst_tile_k,
)
self.cta_tile_shape_mnk = (
self.mma_tiler[0] // cute.size(tiled_mma.thr_id.shape),
self.mma_tiler[1],
self.mma_tiler[2],
)
self.epi_tile = (self.cta_tile_shape_mnk[0], self.epi_tile_n)
self.cluster_layout_vmnk = cute.tiled_divide(
cute.make_layout((*self.cluster_shape_mn, 1)),
(tiled_mma.thr_id.shape,),
)
self.num_mcast_ctas_a = cute.size(self.cluster_layout_vmnk.shape[2])
self.num_mcast_ctas_b = cute.size(self.cluster_layout_vmnk.shape[1])
self.is_a_mcast = self.num_mcast_ctas_a > 1
self.is_b_mcast = self.num_mcast_ctas_b > 1
self.smem_capacity = utils.get_smem_capacity_in_bytes()
# A/B stage count: A tile + TWO B tiles per stage.
a_smem_layout_one = utils.sm100.make_smem_layout_a(
tiled_mma, self.mma_tiler, self.a_dtype, 1
)
b_smem_layout_one = utils.sm100.make_smem_layout_b(
tiled_mma, self.mma_tiler, self.b_dtype, 1
)
ab_bytes_per_stage = cute.size_in_bytes(
self.a_dtype, a_smem_layout_one
) + 2 * cute.size_in_bytes(self.b_dtype, b_smem_layout_one)
mbar_helpers_bytes = 1024
self.num_ab_stage = (
self.smem_capacity // self.occupancy - mbar_helpers_bytes
) // ab_bytes_per_stage
self.a_smem_layout_staged = utils.sm100.make_smem_layout_a(
tiled_mma, self.mma_tiler, self.a_dtype, self.num_ab_stage
)
self.b_smem_layout_staged = utils.sm100.make_smem_layout_b(
tiled_mma, self.mma_tiler, self.b_dtype, self.num_ab_stage
)
# TMEM columns for num_acc_stage accumulators.
acc_shape = tiled_mma.partition_shape_C(self.mma_tiler[:2])
tCtAcc_fake = tiled_mma.make_fragment_C(
cute.append(acc_shape, self.num_acc_stage)
)
self.num_tmem_alloc_cols = utils.get_num_tmem_alloc_cols(
tCtAcc_fake, arch=self.arch
)
@cute.jit
def __call__(
self,
a: cute.Tensor, # (T_perm, K, 1), k-major
bp: cute.Tensor, # (2I, K, E) packed gate/up, n-major
c: cute.Tensor, # (T_perm, I, 1), n-major
offs: cute.Tensor, # (E+1,) int32 prefix sums
max_active_clusters: cutlass.Constexpr,
stream: cuda.CUstream,
):
self.a_dtype: Type[cutlass.Numeric] = a.element_type
self.b_dtype: Type[cutlass.Numeric] = bp.element_type
self.c_dtype: Type[cutlass.Numeric] = c.element_type
self.a_major_mode = utils.LayoutEnum.from_tensor(a).mma_major_mode()
self.b_major_mode = utils.LayoutEnum.from_tensor(bp).mma_major_mode()
self.c_layout = utils.LayoutEnum.from_tensor(c)
tiled_mma = self._create_tiled_mma()
self._setup_attributes()
atom_thr_size = cute.size(tiled_mma.thr_id.shape)
# TMA atoms for A / Bg / Bu (global descriptors; no per-expert maps).
a_op = utils.sm100.cluster_shape_to_tma_atom_A(
self.cluster_shape_mn, tiled_mma.thr_id
)
a_smem_layout = cute.slice_(self.a_smem_layout_staged, (None, None, None, 0))
tma_atom_a, tma_tensor_a = cute.nvgpu.make_tiled_tma_atom_A(
a_op, a, a_smem_layout, self.mma_tiler, tiled_mma,
self.cluster_layout_vmnk.shape,
)
b_op = utils.sm100.cluster_shape_to_tma_atom_B(
self.cluster_shape_mn, tiled_mma.thr_id
)
b_smem_layout = cute.slice_(self.b_smem_layout_staged, (None, None, None, 0))
tma_atom_b, tma_tensor_b = cute.nvgpu.make_tiled_tma_atom_B(
b_op, bp, b_smem_layout, self.mma_tiler, tiled_mma,
self.cluster_layout_vmnk.shape,
)
# Fake C view over the packed GEMM N domain (extent 2I): packed col
# p = tile*MMA_N + h*(MMA_N/2) + col maps to out col tile*(MMA_N/2) +
# col via a stride-0 middle mode. Gate/up halves of an MMA tile thus
# address the SAME output columns; the epilogue only stores the gate
# half after combining.
half_n = self.mma_tiler[1] // 2
n_packed_tiles = cute.size(bp.shape, mode=[0]) // self.mma_tiler[1]
c_fake_layout = cute.make_layout(
(
cute.size(c.shape, mode=[0]),
(half_n, 2, n_packed_tiles),
1,
),
stride=(
cute.size(c.layout.stride, mode=[0]),
(1, 0, half_n),
0,
),
)
c_fake = cute.make_tensor(c.iterator, c_fake_layout)
a_copy_size = cute.size_in_bytes(self.a_dtype, a_smem_layout)
b_copy_size = cute.size_in_bytes(self.b_dtype, b_smem_layout)
self.num_tma_load_bytes = (a_copy_size + b_copy_size) * atom_thr_size
grid = (
self.cluster_shape_mn[0],
self.cluster_shape_mn[1],
max_active_clusters,
)
self.kernel(
tiled_mma,
tma_atom_a, tma_tensor_a,
tma_atom_b, tma_tensor_b,
c_fake,
offs,
self.cluster_layout_vmnk,
self.a_smem_layout_staged,
self.b_smem_layout_staged,
self.epi_tile,
).launch(
grid=grid,
block=[self.threads_per_cta, 1, 1],
cluster=(*self.cluster_shape_mn, 1),
stream=stream,
)
return
@cute.kernel
def kernel(
self,
tiled_mma: cute.TiledMma,
tma_atom_a: cute.CopyAtom,
mA_mkl: cute.Tensor,
tma_atom_b: cute.CopyAtom,
mB_nkl: cute.Tensor,
mC_mnl: cute.Tensor, # fake packed-N view (stride-0 gate/up halves)
offs: cute.Tensor,
cluster_layout_vmnk: cute.Layout,
a_smem_layout_staged: cute.ComposedLayout,
b_smem_layout_staged: cute.ComposedLayout,
epi_tile: cute.Tile,
):
warp_idx = cute.arch.warp_idx()
warp_idx = cute.arch.make_warp_uniform(warp_idx)
if warp_idx == self.tma_warp_id:
cpasync.prefetch_descriptor(tma_atom_a)
cpasync.prefetch_descriptor(tma_atom_b)
use_2cta_instrs = cute.size(tiled_mma.thr_id.shape) == 2
bidx, bidy, bidz = cute.arch.block_idx()
gdimx, gdimy, gdimz = cute.arch.grid_dim()
mma_tile_coord_v = bidx % cute.size(tiled_mma.thr_id.shape)
is_leader_cta = mma_tile_coord_v == 0
cta_rank_in_cluster = cute.arch.make_warp_uniform(
cute.arch.block_idx_in_cluster()
)
block_in_cluster_coord_vmnk = cluster_layout_vmnk.get_flat_coord(
cta_rank_in_cluster
)
tidx, _, _ = cute.arch.thread_idx()
# Grouped-tile-space constants. The scan walks CLUSTER tiles: a
# cluster covers (cluster_m_pairs x cluster_n) MMA tiles, where
# cluster_m_pairs = cluster_shape_m / atom_thr_size (2-CTA MMA pairs).
num_clusters = Int32(gdimz)
cluster_start = Int32(bidz)
E = Int32(cute.size(offs.shape) - 1)
MMA_M = self.mma_tiler[0]
MMA_N = self.mma_tiler[1]
atom_thr = cute.size(tiled_mma.thr_id.shape)
cluster_m_pairs = self.cluster_shape_mn[0] // atom_thr
cluster_n = self.cluster_shape_mn[1]
CL_M = MMA_M * cluster_m_pairs
CL_N = MMA_N * cluster_n
# This CTA's mma-pair coords inside the cluster
pair_m_in_cluster = block_in_cluster_coord_vmnk[1]
n_in_cluster = block_in_cluster_coord_vmnk[2]
N_packed = Int32(cute.size(mB_nkl.shape, mode=[0]))
HALF_N = self.mma_tiler[1] // 2
I_out = N_packed // 2
n_tile_cnt = (N_packed + CL_N - 1) // CL_N
@cute.struct
class SharedStorage:
ab_full_mbar_ptr: cute.struct.MemRange[cutlass.Int64, self.num_ab_stage * 2]
acc_full_mbar_ptr: cute.struct.MemRange[
cutlass.Int64, self.num_acc_stage * 2
]
tmem_dealloc_mbar: cutlass.Int64
tmem_holding_buf: cutlass.Int32
smem = utils.SmemAllocator()
storage = smem.allocate(SharedStorage)
ab_pipeline_producer_group = pipeline.CooperativeGroup(pipeline.Agent.Thread)
num_tma_producer = self.num_mcast_ctas_a + self.num_mcast_ctas_b - 1
ab_pipeline_consumer_group = pipeline.CooperativeGroup(
pipeline.Agent.Thread, num_tma_producer
)
ab_producer, ab_consumer = pipeline.PipelineTmaUmma.create(
barrier_storage=storage.ab_full_mbar_ptr.data_ptr(),
num_stages=self.num_ab_stage,
producer_group=ab_pipeline_producer_group,
consumer_group=ab_pipeline_consumer_group,
tx_count=self.num_tma_load_bytes,
cta_layout_vmnk=cluster_layout_vmnk,
defer_sync=True,
).make_participants()
acc_pipeline_producer_group = pipeline.CooperativeGroup(pipeline.Agent.Thread)
num_acc_consumer_threads = len(self.epilogue_warp_id) * (
2 if use_2cta_instrs else 1
)
acc_pipeline_consumer_group = pipeline.CooperativeGroup(
pipeline.Agent.Thread, num_acc_consumer_threads
)
acc_pipeline = pipeline.PipelineUmmaAsync.create(
barrier_storage=storage.acc_full_mbar_ptr.data_ptr(),
num_stages=self.num_acc_stage,
producer_group=acc_pipeline_producer_group,
consumer_group=acc_pipeline_consumer_group,
cta_layout_vmnk=cluster_layout_vmnk,
defer_sync=True,
)
tmem_alloc_barrier = pipeline.NamedBarrier(
barrier_id=self.tmem_alloc_sync_bar_id,
num_threads=32 * len((self.mma_warp_id, *self.epilogue_warp_id)),
)
tmem_dealloc_barrier = pipeline.NamedBarrier(
barrier_id=self.tmem_dealloc_sync_bar_id,
num_threads=32 * len(self.epilogue_warp_id),
)
tmem = utils.TmemAllocator(
storage.tmem_holding_buf.ptr,
barrier_for_retrieve=tmem_alloc_barrier,
allocator_warp_id=self.epilogue_warp_id[0],
is_two_cta=use_2cta_instrs,
two_cta_tmem_dealloc_mbar_ptr=storage.tmem_dealloc_mbar.ptr,
)
pipeline_init_arrive(cluster_shape_mn=cluster_layout_vmnk, is_relaxed=True)
sA = smem.allocate_tensor(
element_type=self.a_dtype,
layout=a_smem_layout_staged.outer,
byte_alignment=128,
swizzle=a_smem_layout_staged.inner,
)
sB = smem.allocate_tensor(
element_type=self.b_dtype,
layout=b_smem_layout_staged.outer,
byte_alignment=128,
swizzle=b_smem_layout_staged.inner,
)
a_full_mcast_mask = None
b_full_mcast_mask = None
if cutlass.const_expr(self.is_a_mcast or self.is_b_mcast or use_2cta_instrs):
a_full_mcast_mask = cpasync.create_tma_multicast_mask(
cluster_layout_vmnk, block_in_cluster_coord_vmnk, mcast_mode=2
)
b_full_mcast_mask = cpasync.create_tma_multicast_mask(
cluster_layout_vmnk, block_in_cluster_coord_vmnk, mcast_mode=1
)
thr_mma = tiled_mma.get_slice(mma_tile_coord_v)
# MMA fragments over smem
tCrA = tiled_mma.make_fragment_A(sA)
tCrB = tiled_mma.make_fragment_B(sB)
acc_shape = tiled_mma.partition_shape_C(self.mma_tiler[:2])
tCtAcc_fake = tiled_mma.make_fragment_C(
cute.append(acc_shape, self.num_acc_stage)
)
# K tiles (H is uniform across experts)
K_total = Int32(cute.size(mA_mkl.shape, mode=[1]))
k_tile_cnt = (K_total + self.mma_tiler[2] - 1) // self.mma_tiler[2]
# Hoisted B partition (B has a real L=expert mode; no domain offset).
gB_nkl = cute.local_tile(
mB_nkl, cute.slice_(self.mma_tiler, (0, None, None)), (None, None, None)
)
tCgB = thr_mma.partition_B(gB_nkl)
b_cta_layout = cute.make_layout(
cute.slice_(cluster_layout_vmnk, (0, None, 0, 0)).shape
)
tBsB, tBgB = cpasync.tma_partition(
tma_atom_b,
block_in_cluster_coord_vmnk[1],
b_cta_layout,
cute.group_modes(sB, 0, 3),
cute.group_modes(tCgB, 0, 3),
)
a_cta_layout = cute.make_layout(
cute.slice_(cluster_layout_vmnk, (0, 0, None, 0)).shape
)
pipeline_init_wait(cluster_shape_mn=cluster_layout_vmnk)
#
# TMA warp
#
if warp_idx == self.tma_warp_id:
tile_idx = Int32(cluster_start)
e = Int32(0)
tiles_before = Int32(0)
cnt0 = offs[1] - offs[0]
m_cnt = (cnt0 + CL_M - 1) // CL_M
e_tiles = m_cnt * n_tile_cnt
# advance to first tile
while (tile_idx >= tiles_before + e_tiles) and (e < E):
e += 1
tiles_before += e_tiles
e_tiles = Int32(0)
if e < E:
cnt = offs[e + 1] - offs[e]
m_cnt = (cnt + CL_M - 1) // CL_M
e_tiles = m_cnt * n_tile_cnt
while e < E:
local = tile_idx - tiles_before
m_idx = Int32(0)
n_idx = Int32(0)
if cutlass.const_expr(self.raster == 1):
m_idx = local % m_cnt
n_idx = local // m_cnt
elif cutlass.const_expr(self.raster == 2):
n_idx = local % n_tile_cnt
m_idx = local // n_tile_cnt
elif cutlass.const_expr(self.raster == 3):
NG: cutlass.Constexpr = self.raster_group
if m_cnt <= n_tile_cnt:
grp = local // (NG * m_cnt)
first_n = grp * NG
gsz = min(n_tile_cnt - first_n, NG)
ing = local % (NG * m_cnt)
n_idx = first_n + ing % gsz
m_idx = ing // gsz
else:
grp = local // (NG * n_tile_cnt)
first_m = grp * NG
gsz = min(m_cnt - first_m, NG)
ing = local % (NG * n_tile_cnt)
m_idx = first_m + ing % gsz
n_idx = ing // gsz
else:
if m_cnt <= n_tile_cnt:
m_idx = local % m_cnt
n_idx = local // m_cnt
else:
n_idx = local % n_tile_cnt
m_idx = local // n_tile_cnt
m0 = offs[e] + m_idx * CL_M + pair_m_in_cluster * MMA_M
# Per-tile A partition at dynamic row offset
mA_off = cute.domain_offset((m0, 0, 0), mA_mkl)
gA_mkl = cute.local_tile(
mA_off,
cute.slice_(self.mma_tiler, (None, 0, None)),
(None, None, None),
)
tCgA = thr_mma.partition_A(gA_mkl)
tAsA, tAgA = cpasync.tma_partition(
tma_atom_a,
block_in_cluster_coord_vmnk[2],
a_cta_layout,
cute.group_modes(sA, 0, 3),
cute.group_modes(tCgA, 0, 3),
)
tAgA_slice = tAgA[(None, 0, None, 0)]
n_mma_idx = n_idx * cluster_n + n_in_cluster
tBgB_slice = tBgB[(None, n_mma_idx, None, e)]
ab_producer.reset()
peek_ab_empty_status = ab_producer.try_acquire()
for k_tile in cutlass.range(0, k_tile_cnt, 1, unroll=1):
handle = ab_producer.acquire_and_advance(peek_ab_empty_status)
cute.copy(
tma_atom_a,
tAgA_slice[(None, handle.count)],
tAsA[(None, handle.index)],
tma_bar_ptr=handle.barrier,
mcast_mask=a_full_mcast_mask,
)
cute.copy(
tma_atom_b,
tBgB_slice[(None, handle.count)],
tBsB[(None, handle.index)],
tma_bar_ptr=handle.barrier,
mcast_mask=b_full_mcast_mask,
)
peek_ab_empty_status = cutlass.Boolean(1)
if handle.count + 1 < k_tile_cnt:
peek_ab_empty_status = ab_producer.try_acquire()
# advance scan
tile_idx += num_clusters
while (tile_idx >= tiles_before + e_tiles) and (e < E):
e += 1
tiles_before += e_tiles
e_tiles = Int32(0)
if e < E:
cnt = offs[e + 1] - offs[e]
m_cnt = (cnt + CL_M - 1) // CL_M
e_tiles = m_cnt * n_tile_cnt
ab_producer.tail()
#
# MMA warp
#
if warp_idx == self.mma_warp_id:
tmem.wait_for_alloc()
tmem_ptr = tmem.retrieve_ptr(self.acc_dtype)
tCtAcc_base = cute.make_tensor(tmem_ptr, tCtAcc_fake.layout)
acc_producer_state = pipeline.make_pipeline_state(
pipeline.PipelineUserType.Producer, self.num_acc_stage
)
tile_idx = Int32(cluster_start)
e = Int32(0)
tiles_before = Int32(0)
cnt0 = offs[1] - offs[0]
m_cnt = (cnt0 + CL_M - 1) // CL_M
e_tiles = m_cnt * n_tile_cnt
while (tile_idx >= tiles_before + e_tiles) and (e < E):
e += 1
tiles_before += e_tiles
e_tiles = Int32(0)
if e < E:
cnt = offs[e + 1] - offs[e]
m_cnt = (cnt + CL_M - 1) // CL_M
e_tiles = m_cnt * n_tile_cnt
while e < E:
if is_leader_cta:
tCtAcc = tCtAcc_base[
(None, None, None, acc_producer_state.index)
]
ab_consumer.reset()
peek_ab_full_status = cutlass.Boolean(1)
if k_tile_cnt > 0:
peek_ab_full_status = ab_consumer.try_wait()
acc_pipeline.producer_acquire(acc_producer_state)
for k_tile in cutlass.range(0, k_tile_cnt, 1, unroll=1):
handle = ab_consumer.wait_and_advance(peek_ab_full_status)
tile_crd = (None, None, None, handle.index)
tiled_mma.set(tcgen05.Field.ACCUMULATE, k_tile != 0)
cute.gemm(
tiled_mma, tCtAcc, tCrA[tile_crd], tCrB[tile_crd],
tCtAcc,
)
handle.release()
peek_ab_full_status = cutlass.Boolean(1)
if handle.count + 1 < k_tile_cnt:
peek_ab_full_status = ab_consumer.try_wait()
acc_pipeline.producer_commit(acc_producer_state)
acc_producer_state.advance()
tile_idx += num_clusters
while (tile_idx >= tiles_before + e_tiles) and (e < E):
e += 1
tiles_before += e_tiles
e_tiles = Int32(0)
if e < E:
cnt = offs[e + 1] - offs[e]
m_cnt = (cnt + CL_M - 1) // CL_M
e_tiles = m_cnt * n_tile_cnt
acc_pipeline.producer_tail(acc_producer_state)
#
# Epilogue warps
#
if warp_idx < self.mma_warp_id:
tmem.allocate(self.num_tmem_alloc_cols)
tmem.wait_for_alloc()
tmem_ptr = tmem.retrieve_ptr(self.acc_dtype)
tCtAcc_base = cute.make_tensor(tmem_ptr, tCtAcc_fake.layout)
# ((AM, MM), (AN, MN), 2*STAGE)
tCtAcc_t = transform_partitioned_tensor_layout(tCtAcc_base)
# TMEM -> register tiled copy (built once; per-tile invariant)
copy_atom_t2r = get_tmem_load_op(
self.cta_tile_shape_mnk,
self.c_layout,
self.c_dtype,
self.acc_dtype,
epi_tile,
use_2cta_instrs,
)
# (EPI_TILE_M, EPI_TILE_N, EPI_M, EPI_N, 2*STAGE)
tAcc_epi = cute.flat_divide(tCtAcc_t, epi_tile)
tiled_copy_t2r = tcgen05.make_tmem_copy(
copy_atom_t2r, tAcc_epi[(None, None, 0, 0, 0)]
)
thr_copy_t2r = tiled_copy_t2r.get_slice(tidx)
# (T2R, T2R_M, T2R_N, EPI_M, EPI_N, 2*STAGE)
tTR_tAcc = thr_copy_t2r.partition_S(tAcc_epi)
# Identity tensor over one mma tile for predication (per-tile
# coords are tile-local).
cC = cute.make_identity_tensor(
(self.mma_tiler[0], self.mma_tiler[1], 1)
)
gC_c = cute.local_tile(
cC, cute.slice_(self.mma_tiler, (None, None, 0)), (0, 0, 0)
)
tCcC = thr_mma.partition_C(gC_c)
tCcC_t = transform_partitioned_tensor_layout(tCcC)
cC_epi = cute.flat_divide(tCcC_t, epi_tile)
# (T2R, T2R_M, T2R_N, EPI_M, EPI_N)
tTR_cC = thr_copy_t2r.partition_D(cC_epi)
# Register fragments
tTR_rAccG = cute.make_rmem_tensor(
tTR_cC[(None, None, None, 0, 0)].shape, self.acc_dtype
)
tTR_rAccU = cute.make_rmem_tensor(
tTR_cC[(None, None, None, 0, 0)].shape, self.acc_dtype
)
tTR_rC = cute.make_rmem_tensor(tTR_rAccG.shape, self.c_dtype)
acc_consumer_state = pipeline.make_pipeline_state(
pipeline.PipelineUserType.Consumer, self.num_acc_stage
)
subtile_cnt = cute.size(tTR_tAcc.shape, mode=[3]) * cute.size(
tTR_tAcc.shape, mode=[4]
)
tile_idx = Int32(cluster_start)
e = Int32(0)
tiles_before = Int32(0)
cnt0 = offs[1] - offs[0]
m_cnt = (cnt0 + CL_M - 1) // CL_M
e_tiles = m_cnt * n_tile_cnt
while (tile_idx >= tiles_before + e_tiles) and (e < E):
e += 1
tiles_before += e_tiles
e_tiles = Int32(0)
if e < E:
cnt = offs[e + 1] - offs[e]
m_cnt = (cnt + CL_M - 1) // CL_M
e_tiles = m_cnt * n_tile_cnt
while e < E:
local = tile_idx - tiles_before
m_idx = Int32(0)
n_idx = Int32(0)
if cutlass.const_expr(self.raster == 1):
m_idx = local % m_cnt
n_idx = local // m_cnt
elif cutlass.const_expr(self.raster == 2):
n_idx = local % n_tile_cnt
m_idx = local // n_tile_cnt
elif cutlass.const_expr(self.raster == 3):
NG: cutlass.Constexpr = self.raster_group
if m_cnt <= n_tile_cnt:
grp = local // (NG * m_cnt)
first_n = grp * NG
gsz = min(n_tile_cnt - first_n, NG)
ing = local % (NG * m_cnt)
n_idx = first_n + ing % gsz
m_idx = ing // gsz
else:
grp = local // (NG * n_tile_cnt)
first_m = grp * NG
gsz = min(m_cnt - first_m, NG)
ing = local % (NG * n_tile_cnt)
m_idx = first_m + ing % gsz
n_idx = ing // gsz
else:
if m_cnt <= n_tile_cnt:
m_idx = local % m_cnt
n_idx = local // m_cnt
else:
n_idx = local % n_tile_cnt
m_idx = local // n_tile_cnt
m0 = offs[e] + m_idx * CL_M + pair_m_in_cluster * MMA_M
rows_left = offs[e + 1] - m0
# Per-tile C partition at dynamic ROW offset only (the packed
# N mode is tile-aligned; the fake stride-0 half mode maps
# gate/up packed columns onto the same output columns).
mC_off = _domain_offset_aligned((m0, 0, 0), mC_mnl)
gC = cute.local_tile(
mC_off,
cute.slice_(self.mma_tiler, (None, None, 0)),
(0, n_idx * cluster_n + n_in_cluster, 0),
)
tCgC = thr_mma.partition_C(gC)
tCgC_t = transform_partitioned_tensor_layout(tCgC)
gC_epi = cute.flat_divide(tCgC_t, epi_tile)
# (T2R, T2R_M, T2R_N, EPI_M, EPI_N)
tTR_gC = thr_copy_t2r.partition_D(gC_epi)
# (T2R, T2R_M, T2R_N, EPI_M, EPI_N) single accumulator
tTR_tAcc_s = tTR_tAcc[
(None, None, None, None, None, acc_consumer_state.index)
]
tTR_tAcc_s = cute.group_modes(tTR_tAcc_s, 3, cute.rank(tTR_tAcc_s))
tTR_gC_g = cute.group_modes(tTR_gC, 3, cute.rank(tTR_gC))
tTR_cC_g = cute.group_modes(tTR_cC, 3, cute.rank(tTR_cC))
out_col0 = (n_idx * cluster_n + n_in_cluster) * HALF_N
acc_pipeline.consumer_wait(acc_consumer_state)
for pair_idx in cutlass.range(subtile_cnt // 2, unroll=1):
# gate subtile = pair_idx, up subtile = pair_idx + half
cute.copy(
tiled_copy_t2r,
tTR_tAcc_s[(None, None, None, pair_idx)],
tTR_rAccG,
)
cute.copy(
tiled_copy_t2r,
tTR_tAcc_s[
(None, None, None, pair_idx + subtile_cnt // 2)
],
tTR_rAccU,
)
if pair_idx == subtile_cnt // 2 - 1:
cute.arch.fence_view_async_tmem_load()
with cute.arch.elect_one():
acc_pipeline.consumer_release(acc_consumer_state)
acc_consumer_state.advance()
g = tTR_rAccG.load()
u = tTR_rAccU.load()
# silu(g) * u in fp32; sigmoid(x) = 0.5*tanh(x/2) + 0.5
# (tanh.approx.f32 is a single SFU op vs exp+rcp = two).
import cutlass.cute.math as cute_math
sig = cute_math.tanh(g * 0.5, fastmath=True) * 0.5 + 0.5
out_vec = g * sig * u
tTR_rC.store(out_vec.to(self.c_dtype))
# Predication: tile-local coords vs expert row bound and
# true output column bound (gate-half coords are the
# tile-local output columns).
tTR_cC_sub = tTR_cC_g[(None, None, None, pair_idx)]
tTR_gC_sub = tTR_gC_g[(None, None, None, pair_idx)]
pred_shape = (1, *tTR_cC_sub.shape[1:])
pred_C = cute.make_rmem_tensor(pred_shape, Boolean)
for m_i in cutlass.range_constexpr(tTR_cC_sub.shape[1]):
for n_i in cutlass.range_constexpr(tTR_cC_sub.shape[2]):
crd = tTR_cC_sub[(0, m_i, n_i)]
pred_C[(0, m_i, n_i)] = (
crd[0] < rows_left
) and (out_col0 + crd[1] < I_out)
# Vectorized predicated store
mclD = cute.max_common_layout(tTR_rC.layout, tTR_gC_sub.layout)
num_bits = min(cute.size(mclD) * self.c_dtype.width, 128)
simt_atom = cute.make_copy_atom(
cute.nvgpu.CopyR2GOp(),
self.c_dtype,
num_bits_per_copy=num_bits,
l1c_evict_priority=CacheEvictionPriority.NO_ALLOCATE,
)
cute.copy(simt_atom, tTR_rC, tTR_gC_sub, pred=pred_C)
tile_idx += num_clusters
while (tile_idx >= tiles_before + e_tiles) and (e < E):
e += 1
tiles_before += e_tiles
e_tiles = Int32(0)
if e < E:
cnt = offs[e + 1] - offs[e]
m_cnt = (cnt + CL_M - 1) // CL_M
e_tiles = m_cnt * n_tile_cnt
tmem_dealloc_barrier.arrive_and_wait()
tmem.relinquish_alloc_permit()
tmem.free(tmem_ptr)
# ---------------------------------------------------------------------------
# Host-side wrapper
# ---------------------------------------------------------------------------
_COMPILE_CACHE = {}
@cute.jit
def _gg_entry(
kernel_op: cutlass.Constexpr,
a3: cute.Tensor, # (1, T_perm, H)
bp3: cute.Tensor, # (E, H, 2I)
c3: cute.Tensor, # (1, T_perm, I)
offs: cute.Tensor,
max_active_clusters: cutlass.Constexpr,
stream: cuda.CUstream,
):
a = cute.make_tensor(a3.iterator, cute.select(a3.layout, mode=[1, 2, 0]))
bp = cute.make_tensor(bp3.iterator, cute.select(bp3.layout, mode=[2, 1, 0]))
c = cute.make_tensor(c3.iterator, cute.select(c3.layout, mode=[1, 2, 0]))
kernel_op(a, bp, c, offs, max_active_clusters, stream)
def gg_swiglu_packed(
x: "torch.Tensor", # (T_perm, H) bf16
wp: "torch.Tensor", # (E, H, 2I) packed gate/up in 128-col blocks
offs: "torch.Tensor", # (E+1,) int32
out: "torch.Tensor" = None, # (T_perm, I) bf16
mma_tiler_mn=(256, 256),
cluster_shape_mn=(2, 1),
use_2cta_instrs=True,
epi_tile_n=64,
num_acc_stage=2,
raster=0,
raster_group=6,
):
import torch
from cutlass.cute.runtime import from_dlpack
T_perm, H = x.shape
E, _, I2 = wp.shape
I = I2 // 2
if out is None:
out = torch.empty(T_perm, I, dtype=x.dtype, device=x.device)
x3 = x.view(1, T_perm, H)
c3 = out.view(1, T_perm, I)
a_ = from_dlpack(x3, assumed_align=16)
bp_ = from_dlpack(wp, assumed_align=16)
c_ = from_dlpack(c3, assumed_align=16)
offs_ = from_dlpack(offs, assumed_align=4)
key = (T_perm, H, I, E, mma_tiler_mn, cluster_shape_mn, use_2cta_instrs,
epi_tile_n, num_acc_stage, raster, raster_group)
compiled = _COMPILE_CACHE.get(key)
if compiled is None:
kernel = GroupedGemmSwiGLUKernel(
use_2cta_instrs=use_2cta_instrs,
mma_tiler_mn=mma_tiler_mn,
cluster_shape_mn=cluster_shape_mn,
epi_tile_n=epi_tile_n,
num_acc_stage=num_acc_stage,
raster=raster,
raster_group=raster_group,
)
max_active_clusters = utils.HardwareInfo().get_max_active_clusters(
cluster_shape_mn[0] * cluster_shape_mn[1]
)
import torch as _t
stream = cuda.CUstream(_t.cuda.current_stream().cuda_stream)
compiled = cute.compile(
_gg_entry, kernel, a_, bp_, c_, offs_, max_active_clusters,
stream
)
_COMPILE_CACHE[key] = compiled
import torch as _t
stream = cuda.CUstream(_t.cuda.current_stream().cuda_stream)
compiled(a_, bp_, c_, offs_, stream)
return out
# ===========================================================================
# Triton fallback path
# ===========================================================================
_PACK_BLOCK = 128 # column-block granularity of the gate/up interleave
_PACK_BLOCK_C = tl.constexpr(_PACK_BLOCK) # constexpr alias for @jit kernels
def _alloc_fn(size: int, alignment: int, stream):
return torch.empty(size, device="cuda", dtype=torch.int8)
triton.set_allocator(_alloc_fn)
# NOTE: no @triton.autotune here on purpose. Triton 3.6's automatic warp
# specialization deadlocks on SM100 for some (num_stages, K-trip-count)
# combinations (e.g. BLOCK_M=128/ns=3/ws=1 hangs on H=2048 shapes), and
# autotune *executes* every candidate config, so a single hanging config
# would brick the run. The config below is verified hang-free and fastest
# on all deck shapes.
@triton.jit
def _grouped_gemm_swiglu_packed(
x_ptr,
wp_ptr,
out_ptr,
offs_ptr,
T_perm,
H,
I_dim,
E,
stride_xm,
stride_we,
stride_wk,
stride_om,
NUM_SMS: tl.constexpr,
BLOCK_M: tl.constexpr,
BLOCK_K: tl.constexpr,
WS: tl.constexpr,
):
BLOCK_N: tl.constexpr = 2 * _PACK_BLOCK_C
HALF: tl.constexpr = _PACK_BLOCK_C
start_pid = tl.program_id(0)
num_n_tiles = tl.cdiv(2 * I_dim, BLOCK_N)
a_desc = tl.make_tensor_descriptor(
x_ptr,
shape=[T_perm, H],
strides=[stride_xm, 1],
block_shape=[BLOCK_M, BLOCK_K],
)
b_desc = tl.make_tensor_descriptor(
wp_ptr,
shape=[E, H, 2 * I_dim],
strides=[stride_we, stride_wk, 1],
block_shape=[1, BLOCK_K, BLOCK_N],
)
tile_idx = start_pid
last_end = 0
for e in range(E):
e_start = tl.load(offs_ptr + e)
e_end = tl.load(offs_ptr + e + 1)
cnt = e_end - e_start
num_m_tiles = tl.cdiv(cnt, BLOCK_M)
num_tiles_e = num_m_tiles * num_n_tiles
while tile_idx >= last_end and tile_idx < last_end + num_tiles_e:
local = tile_idx - last_end
pid_m = local // num_n_tiles
pid_n = local % num_n_tiles
m0 = e_start + pid_m * BLOCK_M
n0 = pid_n * BLOCK_N
acc = tl.zeros((BLOCK_M, BLOCK_N), dtype=tl.float32)
for k in tl.range(0, H, BLOCK_K, warp_specialize=WS):
a = a_desc.load([m0, k])
b = b_desc.load([e, k, n0]).reshape(BLOCK_K, BLOCK_N)
acc = tl.dot(a, b, acc)
g, u = tl.split(acc.reshape(BLOCK_M, 2, HALF).permute(0, 2, 1))
gated = g * tl.sigmoid(g) * u
offs_m = m0 + tl.arange(0, BLOCK_M)
mask_m = offs_m < e_end
offs_n = pid_n * HALF + tl.arange(0, HALF)
o_ptrs = out_ptr + offs_m[:, None] * stride_om + offs_n[None, :]
tl.store(o_ptrs, gated.to(tl.bfloat16), mask=mask_m[:, None])
tile_idx += NUM_SMS
last_end += num_tiles_e
@triton.jit
def _grouped_gemm_swiglu_fallback(
x_ptr,
wg_ptr,
wu_ptr,
out_ptr,
offs_ptr,
T_perm,
H,
I_dim,
E,
stride_xm,
stride_we,
stride_wk,
stride_om,
NUM_SMS: tl.constexpr,
BLOCK_M: tl.constexpr,
BLOCK_N: tl.constexpr,
BLOCK_K: tl.constexpr,
):
"""Fully masked pointer-based variant for shapes where H or I is not
divisible by the tile sizes. Slower, but correct for any shape."""
start_pid = tl.program_id(0)
num_n_tiles = tl.cdiv(I_dim, BLOCK_N)
tile_idx = start_pid
last_end = 0
for e in range(E):
e_start = tl.load(offs_ptr + e)
e_end = tl.load(offs_ptr + e + 1)
cnt = e_end - e_start
num_m_tiles = tl.cdiv(cnt, BLOCK_M)
num_tiles_e = num_m_tiles * num_n_tiles
while tile_idx >= last_end and tile_idx < last_end + num_tiles_e:
local = tile_idx - last_end
pid_m = local // num_n_tiles
pid_n = local % num_n_tiles
offs_m = e_start + pid_m * BLOCK_M + tl.arange(0, BLOCK_M)
mask_m = offs_m < e_end
offs_n = pid_n * BLOCK_N + tl.arange(0, BLOCK_N)
mask_n = offs_n < I_dim
offs_k = tl.arange(0, BLOCK_K)
a_ptrs = x_ptr + offs_m[:, None] * stride_xm + offs_k[None, :]
bg_ptrs = (
wg_ptr + e * stride_we
+ offs_k[:, None] * stride_wk + offs_n[None, :]
)
bu_ptrs = (
wu_ptr + e * stride_we
+ offs_k[:, None] * stride_wk + offs_n[None, :]
)
acc_g = tl.zeros((BLOCK_M, BLOCK_N), dtype=tl.float32)
acc_u = tl.zeros((BLOCK_M, BLOCK_N), dtype=tl.float32)
for k0 in range(0, H, BLOCK_K):
mask_k = (k0 + offs_k) < H
a = tl.load(
a_ptrs, mask=mask_m[:, None] & mask_k[None, :], other=0.0
)
bmask = mask_k[:, None] & mask_n[None, :]
bg = tl.load(bg_ptrs, mask=bmask, other=0.0)
bu = tl.load(bu_ptrs, mask=bmask, other=0.0)
acc_g = tl.dot(a, bg, acc_g)
acc_u = tl.dot(a, bu, acc_u)
a_ptrs += BLOCK_K
bg_ptrs += BLOCK_K * stride_wk
bu_ptrs += BLOCK_K * stride_wk
gated = acc_g * tl.sigmoid(acc_g) * acc_u
o_ptrs = out_ptr + offs_m[:, None] * stride_om + offs_n[None, :]
tl.store(
o_ptrs,
gated.to(tl.bfloat16),
mask=mask_m[:, None] & mask_n[None, :],
)
tile_idx += NUM_SMS
last_end += num_tiles_e
class Model(nn.Module):
"""Drop-in replacement for reference.Model with a fused custom kernel."""
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._num_sms = None
self._w_pack = None
self._w_pack_key = None
self._cute_broken = False
self._out_cache = {}
self._graph_cache = {}
# -- Triton packed-weight cache (fallback path only) -------------------
def _pack_weights(self) -> torch.Tensor:
key = (
self.W_gate.data_ptr(),
self.W_up.data_ptr(),
self.W_gate._version,
self.W_up._version,
)
if self._w_pack is not None and self._w_pack_key == key:
return self._w_pack
E, H, I_dim = self.W_gate.shape
wp = torch.empty(
E, H, 2 * I_dim, dtype=torch.bfloat16, device=self.W_gate.device
)
pb = _PACK_BLOCK
wg = self.W_gate.detach().view(E, H, I_dim // pb, pb)
wu = self.W_up.detach().view(E, H, I_dim // pb, pb)
wpv = wp.view(E, H, I_dim // pb, 2, pb)
wpv[:, :, :, 0, :] = wg
wpv[:, :, :, 1, :] = wu
self._w_pack = wp
self._w_pack_key = key
return wp
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_dim = self.I
E = self.E
x = hidden_states.contiguous()
offs = expert_offsets
if offs.dtype != torch.int32:
offs = offs.to(torch.int32)
offs = offs.contiguous()
# Reuse the output buffer across calls: the kernel overwrites every
# row it owns each call (rows not owned by any expert never change,
# matching the reference's torch.empty semantics), and reuse keeps
# the physical pages stable between timed iterations.
okey = (T_perm, I_dim, x.device)
out = self._out_cache.get(okey)
if out is None:
out = torch.empty(
T_perm, I_dim, dtype=torch.bfloat16, device=x.device
)
self._out_cache[okey] = out
# Primary: CuTe DSL grouped GEMM + fused SwiGLU (SM100 tcgen05).
# Alignment prerequisites: contiguous bf16, 8-element (16B) aligned
# dims for TMA, MMA-tile-aligned I for the epilogue store vectors.
if (
_CUTE_OK
and not self._cute_broken
and x.dtype == torch.bfloat16
and self.W_gate.dtype == torch.bfloat16
and H % 64 == 0
and I_dim % 128 == 0
):
try:
wp = self._pack_weights()
# CUDA-graph the call per distinct (input, weight, offsets,
# output) buffer set: replay re-launches the same kernel on
# the LIVE buffer contents (recompute is verified by in-place
# input mutation in the audit), eliminating host-side tensor
# wrapping + launch overhead inside the timed window.
gkey = (
x.data_ptr(), wp.data_ptr(), offs.data_ptr(),
out.data_ptr(), T_perm, H, I_dim, E,
)
graph = self._graph_cache.get(gkey)
if graph is None:
# Warm/compile outside capture, then capture one call.
gg_swiglu_packed(x, wp, offs, out=out)
torch.cuda.synchronize()
graph = torch.cuda.CUDAGraph()
with torch.cuda.graph(graph):
gg_swiglu_packed(x, wp, offs, out=out)
self._graph_cache[gkey] = graph
graph.replay()
return out
except Exception:
# Never retry a broken path inside a timing loop.
self._cute_broken = True
if self._num_sms is None:
self._num_sms = torch.cuda.get_device_properties(
x.device
).multi_processor_count
num_sms = self._num_sms
grid = (num_sms,)
if I_dim % _PACK_BLOCK == 0 and H % 64 == 0:
wp = self._pack_weights()
_grouped_gemm_swiglu_packed[grid](
x, wp, out, offs,
T_perm, H, I_dim, E,
x.stride(0), wp.stride(0), wp.stride(1), out.stride(0),
NUM_SMS=num_sms,
BLOCK_M=128, BLOCK_K=64, WS=True,
num_stages=4, num_warps=8,
)
else:
_grouped_gemm_swiglu_fallback[grid](
x, self.W_gate, self.W_up, out, offs,
T_perm, H, I_dim, E,
x.stride(0), self.W_gate.stride(0), self.W_gate.stride(1),
out.stride(0),
NUM_SMS=num_sms,
BLOCK_M=128, BLOCK_N=128, BLOCK_K=64,
num_stages=3, num_warps=8,
)
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]
20260719_063909_or-fable_anthropic_claude-fable-5_06_sonic_moe_swiglu