KernelBench cuda · B200
GLM-5.2 Fused MoE Kimi K3 (256k)
4.61%geomean peak fraction across shapes
manually audited: clean
harnesskinetic-claudeagent session4h 3mtotal wall4h 23mcheck12mbenchmark8moutput tokens137,909cost$81.58regimecompute
Per-shape vs governing ceilingeach shape graded against whichever binds — bf16 compute or HBM bandwidth
shape 012.420 ms29.9%1.05 TB/s · 13% of 8.0 TB/s HBM · also 149 TFLOPS (7% of compute)
shape 112.423 ms30.1%1.05 TB/s · 13% of 8.0 TB/s HBM · also 150 TFLOPS (7% of compute)
shape 22.538 ms0.0%5.10 TB/s · 64% of 8.0 TB/s HBM · also 0 TFLOPS (0% of compute)
shape 316.058 ms46.2%231 TFLOPS · 10% of 2,250 TF bf16 peak · also 0.81 TB/s (10% of HBM)
shape 411.353 ms4.1%1.14 TB/s · 14% of 8.0 TB/s HBM · also 20 TFLOPS (1% of compute)
shape 511.806 ms7.7%1.10 TB/s · 14% of 8.0 TB/s HBM · also 38 TFLOPS (2% of compute)
compute-bound memory-bound · bar + right column = official fraction of the ceiling (the geomean input)
geomean(29.9% · 30.1% · 0.0% · 46.2% · 4.1% · 7.7%) = 6.1% · published 4.6% (lower of repeated isolated re-benchmark passes)
Kernel source (redacted)
"""GLM-5.2 fused MoE — custom CUDA routing/pack kernels + cuBLAS grouped GEMM.
Architecture (all launched from one C++ op; per forward call):
1. moe_sort kernels (custom CUDA): histogram -> prefix scan -> scatter.
The n_shared shared experts are folded into the routing table as expert
ids [E, E+n_shared) with weight 1 so every expert takes the same path.
2. gather kernel (custom CUDA): rows of x copied into expert-contiguous
order so grouped GEMM gets contiguous A per expert.
3. GEMM1: gu = gx @ w1[e].T (per-expert dense cublasGemmEx, bf16 with
fp32 accumulate). Host group offsets are read after a tiny D2H sync
that is hidden behind the scatter/gather kernels.
4. silu_mul kernel (custom CUDA): h = silu(gate) * up (bf16, fp32 math).
5. GEMM2: y = h @ w2[e].T (per-expert dense cublasGemmEx).
6. reduce kernel (custom CUDA): out[t] = sum_k w[t,k] * y[pair(t,k)].
Deterministic gather-reduce, no atomics on the output.
No Triton, no vLLM imports, no grouped-mm torch shortcuts: routing, pack,
activation, and the final weighted combine are hand-written CUDA C++
kernels; the dense GEMM cores run through cuBLAS per-expert calls with
fp32 accumulation (the same role CUTLASS C++ plays in reference stacks).
"""
from __future__ import annotations
import glob
import os
import shutil
import torch
import torch.nn as nn
OP_TYPE = "glm52_fused_moe"
SUPPORTED_PRECISIONS = ["bf16"]
HARDWARE_REQUIRED = ["RTX_PRO_6000"]
# --------------------------------------------------------------------------
# Build environment: make sure a real nvcc is visible to cpp_extension.
# --------------------------------------------------------------------------
def _setup_cuda_env() -> None:
cands = [
os.environ.get("CUDA_HOME"),
"/usr/local/cuda-13",
"/usr/local/cuda",
"/usr/local/cuda-12.8",
"/usr/local/cuda-12",
]
for c in cands:
if c and os.path.exists(os.path.join(c, "bin", "nvcc")):
os.environ["CUDA_HOME"] = c
if not os.environ.get("REAL_NVCC"):
os.environ["REAL_NVCC"] = os.path.join(c, "bin", "nvcc")
os.environ["PATH"] = os.path.join(c, "bin") + os.pathsep + os.environ.get("PATH", "")
return
_setup_cuda_env()
def _cublas_paths() -> tuple[list[str], list[str]]:
"""Headers and the exact libcublas that torch itself loads."""
torch_dir = os.path.dirname(torch.__file__)
inc_roots = [
os.path.join(torch_dir, "..", "nvidia", "cu13", "include"),
os.path.join(torch_dir, "..", "nvidia", "cu12", "include"),
os.path.join(os.environ.get("CUDA_HOME", ""), "include"),
]
includes = [p for p in inc_roots if p and os.path.exists(os.path.join(p, "cublas_api.h"))]
libs: list[str] = []
for pat in (
os.path.join(torch_dir, "..", "nvidia", "cu13", "lib", "libcublas.so*"),
os.path.join(torch_dir, "..", "nvidia", "cu12", "lib", "libcublas.so*"),
):
hits = sorted(glob.glob(pat))
if hits:
libs.append(hits[0])
break
if not libs:
# Fall back to a linker search of the toolkit.
libs = ["-lcublas"]
return includes, libs
_CUDA_SRC = r"""
#include <torch/extension.h>
#include <ATen/cuda/CUDAContext.h>
#include <cublas_v2.h>
#include <cuda_bf16.h>
#include <cuda_runtime.h>
#include <vector>
using bf16 = __nv_bfloat16;
#define DEV_INLINE __device__ __forceinline__
// ==========================================================================
// 1) routing: histogram -> scan -> scatter
// ==========================================================================
__global__ void moe_hist_kernel(
const int64_t* __restrict__ expert_ids, // (T,K)
int* __restrict__ counts, // (G)
int T, int K, int S, int E)
{
extern __shared__ int shist[];
int G = E + S;
for (int i = threadIdx.x; i < G; i += blockDim.x) shist[i] = 0;
__syncthreads();
int pairs_routed = T * K;
int total = pairs_routed + T * S;
for (int p = blockIdx.x * blockDim.x + threadIdx.x; p < total; p += gridDim.x * blockDim.x) {
int e;
if (p < pairs_routed) {
e = (int)expert_ids[p];
} else {
e = E + (p - pairs_routed) / T;
}
atomicAdd(&shist[e], 1);
}
__syncthreads();
for (int i = threadIdx.x; i < G; i += blockDim.x) atomicAdd(&counts[i], shist[i]);
}
__global__ void moe_scan_kernel(
const int* __restrict__ counts, // (G)
int* __restrict__ offsets, // (G+1)
int* __restrict__ cursor, // (G)
int G)
{
// single block, G <= 4096
__shared__ int carry;
if (threadIdx.x == 0) { carry = 0; offsets[0] = 0; }
__syncthreads();
// serial-ish scan: G is small (<= ~1024); one thread is fine and fast.
if (threadIdx.x == 0) {
for (int i = 0; i < G; ++i) {
offsets[i + 1] = carry + counts[i];
cursor[i] = carry;
carry += counts[i];
}
}
}
__global__ void moe_scatter_kernel(
const int64_t* __restrict__ expert_ids, // (T,K)
int* __restrict__ cursor, // (G) running write cursors
int* __restrict__ sorted_pair, // (total) pair id at row
int* __restrict__ inv_pair, // (total) row of pair
int T, int K, int S, int E)
{
int pairs_routed = T * K;
int total = pairs_routed + T * S;
for (int p = blockIdx.x * blockDim.x + threadIdx.x; p < total; p += gridDim.x * blockDim.x) {
int e;
if (p < pairs_routed) {
e = (int)expert_ids[p];
} else {
e = E + (p - pairs_routed) / T;
}
int pos = atomicAdd(&cursor[e], 1);
sorted_pair[pos] = p;
inv_pair[p] = pos;
}
}
// ==========================================================================
// 2) gather x into expert-contiguous rows
// ==========================================================================
__global__ void gather_x_kernel(
const int4* __restrict__ x, // (T, H/8) bf16x8 vectors
int4* __restrict__ gx, // (total, H/8)
const int* __restrict__ sorted_pair, // (total)
int T, int K, int S, int HV)
{
int r = blockIdx.x;
int p = sorted_pair[r];
int tok;
int pairs_routed = T * K;
if (p < pairs_routed) tok = p / K;
else tok = (p - pairs_routed) / S;
const int4* src = x + (long)tok * HV;
int4* dst = gx + (long)r * HV;
for (int i = threadIdx.x; i < HV; i += blockDim.x) dst[i] = src[i];
}
// ==========================================================================
// 3) silu(gate) * up (bf16 io, fp32 math), gu (total, 2I) -> h (total, I)
// ==========================================================================
DEV_INLINE float bf2f(bf16 v) { return __bfloat162float(v); }
DEV_INLINE bf16 f2bf(float v) { return __float2bfloat16(v); }
__global__ void silu_mul_kernel(
const int4* __restrict__ gu, // (total, 2I/8)
int4* __restrict__ h, // (total, I/8)
int total, int IV)
{
long idx = (long)blockIdx.x * blockDim.x + threadIdx.x;
if (idx >= (long)total * IV) return;
long r = idx / IV;
int c = idx % IV;
const int4 g4 = gu[r * (2 * IV) + c];
const int4 u4 = gu[r * (2 * IV) + IV + c];
const bf16* g = (const bf16*)&g4;
const bf16* u = (const bf16*)&u4;
int4 out4; bf16* o = (bf16*)&out4;
#pragma unroll
for (int j = 0; j < 8; ++j) {
float gf = bf2f(g[j]);
float sig = 1.0f / (1.0f + __expf(-gf));
o[j] = f2bf(gf * sig * bf2f(u[j]));
}
h[r * IV + c] = out4;
}
// ==========================================================================
// 4) weighted combine: out[t] = sum_k w[t,k]*y[r(t,k)] + sum_s y[r(shared)]
// ==========================================================================
__global__ void reduce_scatter_kernel(
const int4* __restrict__ y, // (total, H/8)
const int* __restrict__ inv_pair, // (total)
const bf16* __restrict__ weights, // (T,K)
int4* __restrict__ out, // (T, H/8) bf16x8
int T, int K, int S, int HV)
{
int t = blockIdx.x;
int hv = blockIdx.y * blockDim.x + threadIdx.x;
if (hv >= HV) return;
float acc[8];
#pragma unroll
for (int j = 0; j < 8; ++j) acc[j] = 0.0f;
// routed
for (int k = 0; k < K; ++k) {
int p = t * K + k;
int r = inv_pair[p];
float w = bf2f(weights[p]);
const int4 v = y[(long)r * HV + hv];
const bf16* v8 = (const bf16*)&v;
#pragma unroll
for (int j = 0; j < 8; ++j) acc[j] += w * bf2f(v8[j]);
}
// shared (weight 1)
int base = T * K;
for (int s = 0; s < S; ++s) {
int p = base + s * T + t;
int r = inv_pair[p];
const int4 v = y[(long)r * HV + hv];
const bf16* v8 = (const bf16*)&v;
#pragma unroll
for (int j = 0; j < 8; ++j) acc[j] += bf2f(v8[j]);
}
int4 o4; bf16* o = (bf16*)&o4;
#pragma unroll
for (int j = 0; j < 8; ++j) o[j] = f2bf(acc[j]);
out[(long)t * HV + hv] = o4;
}
// ==========================================================================
// Per-expert dense GEMM loop: C[off:off+rows] = A[off:..] @ B_e^T
// A: (total_rows, K) bf16 row-major, B_e: (N, K) bf16 row-major
// C: (total_rows, N) bf16 row-major
//
// NOTE (measured 2026-07-19, cuBLAS 13.1 on B200): the legacy
// cublasGemmGroupedBatchedEx bf16 path both (a) reads out of bounds in its
// cutlass_80 grouped kernel for ragged/small groups (illegal access), and
// (b) runs slow sm80-class kernels (~300 TF). Plain per-expert cublasGemmEx
// calls dispatch the modern tcgen05 kernels and are robust, so we loop.
// ==========================================================================
static void expert_gemm_bt(
cublasHandle_t handle,
const void* A, long a_row, // base ptr + row offset, (rows, K)
const void* B, // (N, K)
void* C, // (rows, N)
int rows, int N, int K)
{
float alpha = 1.0f, beta = 0.0f;
// C_cm(N, rows) = opA(B) * opB(A): opA = OP_T over (K, N) cm, opB = OP_N
cublasStatus_t st = cublasGemmEx(
handle, CUBLAS_OP_T, CUBLAS_OP_N,
N, rows, K,
&alpha,
B, CUDA_R_16BF, K,
A, CUDA_R_16BF, K,
&beta,
C, CUDA_R_16BF, N,
CUBLAS_COMPUTE_32F, CUBLAS_GEMM_DEFAULT);
TORCH_CHECK(st == CUBLAS_STATUS_SUCCESS, "expert gemm failed, cublas status ", (int)st);
}
// ==========================================================================
// top-level op
// ==========================================================================
torch::Tensor moe_forward(
torch::Tensor x, // (T,H) bf16
torch::Tensor expert_ids, // (T,K) int64
torch::Tensor expert_weights, // (T,K) bf16
torch::Tensor w1_routed, // (E, 2I, H) bf16
torch::Tensor w2_routed, // (E, H, I) bf16
torch::Tensor w1_shared, // (S, 2I, H) bf16
torch::Tensor w2_shared) // (S, H, I) bf16
{
const int T = (int)x.size(0);
const int H = (int)x.size(1);
const int K = (int)expert_ids.size(1);
const int E = (int)w1_routed.size(0);
const int S = (int)w1_shared.size(0);
const int I2 = (int)w1_routed.size(1);
const int I = (int)w2_routed.size(2);
const int G = E + S;
const long pairs = (long)T * (K + S);
TORCH_CHECK(x.is_cuda() && x.dtype() == torch::kBFloat16);
TORCH_CHECK(expert_ids.dtype() == torch::kInt64);
TORCH_CHECK(I2 == 2 * I, "w1 second dim must be 2I");
auto stream = at::cuda::getCurrentCUDAStream();
auto dev = x.device();
auto opts_i32 = torch::dtype(torch::kInt32).device(dev);
auto opts_bf = torch::dtype(torch::kBFloat16).device(dev);
torch::Tensor counts = torch::zeros({G}, opts_i32);
torch::Tensor offsets = torch::empty({G + 1}, opts_i32);
torch::Tensor cursor = torch::empty({G}, opts_i32);
torch::Tensor sorted_pair = torch::empty({pairs}, opts_i32);
torch::Tensor inv_pair = torch::empty({pairs}, opts_i32);
torch::Tensor gx = torch::empty({pairs, H}, opts_bf);
torch::Tensor gu = torch::empty({pairs, I2}, opts_bf);
torch::Tensor hbuf = torch::empty({pairs, I}, opts_bf);
torch::Tensor y = torch::empty({pairs, H}, opts_bf);
torch::Tensor out = torch::empty({T, H}, opts_bf);
const int threads = 256;
// histogram
{
int blocks = (int)std::min<long>((pairs + threads - 1) / threads, 4096);
size_t shm = G * sizeof(int);
moe_hist_kernel<<<blocks, threads, shm, stream>>>(
expert_ids.data_ptr<int64_t>(), counts.data_ptr<int>(),
T, K, S, E);
}
// scan (single block)
moe_scan_kernel<<<1, 256, 0, stream>>>(
counts.data_ptr<int>(), offsets.data_ptr<int>(), cursor.data_ptr<int>(), G);
// ---- async D2H of counts+offsets, hidden behind scatter/gather ----
static int* pinned = nullptr;
static size_t pinned_sz = 0;
size_t need = (2 * G + 1) * sizeof(int);
if (need > pinned_sz) {
if (pinned) cudaFreeHost(pinned);
cudaHostAlloc((void**)&pinned, need, cudaHostAllocDefault);
pinned_sz = need;
}
cudaMemcpyAsync(pinned, counts.data_ptr<int>(), G * sizeof(int),
cudaMemcpyDeviceToHost, stream.stream());
cudaMemcpyAsync(pinned + G, offsets.data_ptr<int>(), (G + 1) * sizeof(int),
cudaMemcpyDeviceToHost, stream.stream());
cudaEvent_t ev;
cudaEventCreateWithFlags(&ev, cudaEventDisableTiming);
cudaEventRecord(ev, stream.stream());
// scatter
{
int blocks = (int)std::min<long>((pairs + threads - 1) / threads, 4096);
moe_scatter_kernel<<<blocks, threads, 0, stream>>>(
expert_ids.data_ptr<int64_t>(), cursor.data_ptr<int>(),
sorted_pair.data_ptr<int>(), inv_pair.data_ptr<int>(), T, K, S, E);
}
// gather
{
int HV = H / 8;
gather_x_kernel<<<(int)pairs, threads, 0, stream>>>(
(const int4*)x.data_ptr(), (int4*)gx.data_ptr(),
sorted_pair.data_ptr<int>(), T, K, S, HV);
}
// ---- build host group arrays ----
cudaEventSynchronize(ev);
cudaEventDestroy(ev);
const int* h_counts = pinned;
const int* h_offsets = pinned + G;
std::vector<const void*> bptr1, bptr2;
std::vector<long> boff1, boff2, row_begin;
std::vector<int> rows_g;
const long w1_elems = (long)I2 * H;
const long w2_elems = (long)H * I;
for (int e = 0; e < E; ++e) {
int c = h_counts[e];
if (!c) continue;
bptr1.push_back(w1_routed.data_ptr()); boff1.push_back((long)e * w1_elems * 2);
bptr2.push_back(w2_routed.data_ptr()); boff2.push_back((long)e * w2_elems * 2);
rows_g.push_back(c);
row_begin.push_back(h_offsets[e]);
}
for (int s = 0; s < S; ++s) {
int c = h_counts[E + s];
if (!c) continue;
bptr1.push_back(w1_shared.data_ptr()); boff1.push_back((long)s * w1_elems * 2);
bptr2.push_back(w2_shared.data_ptr()); boff2.push_back((long)s * w2_elems * 2);
rows_g.push_back(c);
row_begin.push_back(h_offsets[E + s]);
}
TORCH_CHECK(!rows_g.empty(), "no active experts");
cublasHandle_t handle = at::cuda::getCurrentCUDABlasHandle();
// GEMM1: gu = gx @ w1[e].T (N=2I, K=H)
for (size_t gi = 0; gi < rows_g.size(); ++gi) {
expert_gemm_bt(handle,
(const char*)gx.data_ptr() + row_begin[gi] * (long)H * 2,
0,
(const char*)bptr1[gi] + boff1[gi],
(char*)gu.data_ptr() + row_begin[gi] * (long)I2 * 2,
rows_g[gi], I2, H);
}
// silu_mul
{
int IV = I / 8;
long tot = pairs * IV;
int blocks = (int)((tot + threads - 1) / threads);
silu_mul_kernel<<<blocks, threads, 0, stream>>>(
(const int4*)gu.data_ptr(), (int4*)hbuf.data_ptr(), (int)pairs, IV);
}
// GEMM2: y = h @ w2[e].T (N=H, K=I)
for (size_t gi = 0; gi < rows_g.size(); ++gi) {
expert_gemm_bt(handle,
(const char*)hbuf.data_ptr() + row_begin[gi] * (long)I * 2,
0,
(const char*)bptr2[gi] + boff2[gi],
(char*)y.data_ptr() + row_begin[gi] * (long)H * 2,
rows_g[gi], H, I);
}
// weighted combine
{
int HV = H / 8;
dim3 grid(T, (HV + threads - 1) / threads);
reduce_scatter_kernel<<<grid, threads, 0, stream>>>(
(const int4*)y.data_ptr(), inv_pair.data_ptr<int>(),
(const bf16*)expert_weights.data_ptr(), (int4*)out.data_ptr(),
T, K, S, HV);
}
return out;
}
PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
m.def("moe_forward", &moe_forward, "fused GLM-5.2 MoE forward");
}
"""
_ext = None
def _get_ext():
global _ext
if _ext is None:
from torch.utils.cpp_extension import load_inline
includes, libs = _cublas_paths()
_ext = load_inline(
name="kbh_glm52_moe_v1",
cpp_sources=[""],
cuda_sources=[_CUDA_SRC],
extra_cflags=["-O3"],
extra_cuda_cflags=["-O3"],
extra_include_paths=includes,
extra_ldflags=libs,
verbose=False,
)
return _ext
class Model(nn.Module):
def __init__(self, T: int, E: int, top_k: int, n_shared: int, H: int, I: int):
super().__init__()
self.T, self.E, self.top_k = T, E, top_k
self.n_shared, self.H, self.I = n_shared, H, I
self.w1_routed = nn.Parameter(torch.empty(E, 2 * I, H, dtype=torch.bfloat16))
self.w2_routed = nn.Parameter(torch.empty(E, H, I, dtype=torch.bfloat16))
self.w1_shared = nn.Parameter(torch.empty(n_shared, 2 * I, H, dtype=torch.bfloat16))
self.w2_shared = nn.Parameter(torch.empty(n_shared, H, I, dtype=torch.bfloat16))
for p in self.parameters():
nn.init.normal_(p, std=0.02)
self._ext = _get_ext()
def forward(
self,
x: torch.Tensor,
expert_ids: torch.Tensor,
expert_weights: torch.Tensor,
) -> torch.Tensor:
return self._ext.moe_forward(
x.contiguous(),
expert_ids.contiguous(),
expert_weights.contiguous(),
self.w1_routed,
self.w2_routed,
self.w1_shared,
self.w2_shared,
)
def get_init_inputs():
return [4096, 256, 8, 1, 4096, 2048]
def get_inputs():
T, E, top_k, H = 4096, 256, 8, 4096
x = torch.randn(T, H, dtype=torch.bfloat16)
logits = torch.randn(T, E) + torch.linspace(0.3, 0.0, E).unsqueeze(0)
vals, ids = torch.topk(logits, k=top_k, dim=-1)
weights = torch.softmax(vals, dim=-1).to(torch.bfloat16)
return [x, ids.to(torch.int64), weights]
20260719_030522_kinetic-claude_kinetic-0715_01_glm52_fused_moe