KernelBench cuda · RTX PRO 6000
GLM-5.2 Fused MoE Claude Sonnet 5.5
11.3%geomean peak fraction across shapes
manually audited: clean
Sonnet 5.5 wrote the GLM-5.2 MoE layer as hand-written CUDA: a device-side counting sort of the routed pairs, two swap-AB bf16 tensor-core grouped GEMMs (silu(gate)*up fused into the first epilogue, routing weights and fp32 atomics into an L2-resident accumulator in the second), the shared expert scheduled as one more expert, and a separate cooperative GEMV kernel for the single-token decode shape.
harnessclaudeagent session4h 5mtotal wall4h 18mcheck3mbenchmark2moutput tokens977,326cost$53.64gpu-lock wait1h 18mgpu-lock held1h 16mregimecompute
Per-shape vs governing ceilingeach shape graded against whichever binds — bf16 compute or HBM bandwidth
shape 08.569 ms43.3%1.52 TB/s · 84% of 1.8 TB/s HBM · also 217 TFLOPS (43% of compute)
shape 18.616 ms43.4%1.51 TB/s · 84% of 1.8 TB/s HBM · also 217 TFLOPS (43% of compute)
shape 20.306 ms0.3%42.25 TB/s · 100% of 1.8 TB/s HBM · also 1 TFLOPS (0% of compute)
shape 313.044 ms56.9%284 TFLOPS · 57% of 500 TF bf16 peak · also 1.00 TB/s (56% of HBM)
shape 47.955 ms5.8%1.63 TB/s · 90% of 1.8 TB/s HBM · also 29 TFLOPS (6% of compute)
shape 58.020 ms11.3%1.61 TB/s · 90% of 1.8 TB/s HBM · also 56 TFLOPS (11% of compute)
compute-bound memory-bound · bar + right column = official fraction of the ceiling (the geomean input)
geomean(43.3% · 43.4% · 0.3% · 56.9% · 5.8% · 11.3%) = 11.3%
Kernel source (redacted)
"""GLM-5.2 fused MoE layer (256 routed experts, top-8, 1 shared expert) in CUDA for SM120 (RTX PRO 6000).
Pipeline (all CUDA, compiled with torch.utils.cpp_extension.load_inline):
* prep_kernel counting sort of the (token, k) pairs by expert + tile tables (device side, no host sync)
* moe_gemm_kernel grouped bf16 GEMM on mma.sync tensor cores. The weights are the MMA "A" operand and the
gathered token rows the "B" operand (swap-AB), so the token dimension is handled in units of
16 (only live 16-token blocks are computed); token tiles are cut at multiples of 32 so the two
token groups of a CTA stay balanced. cp.async 4-stage shared-memory pipeline with
XOR-swizzled tiles and ldmatrix/mma.sync register blocking.
GEMM1: silu(x @ gate.T) * (x @ up.T) -> h (bf16); the shared expert is one more "expert"
GEMM2: routing-weight * (h @ down.T), accumulated with fp32 atomics into an L2-resident
accumulator (access-policy window), then converted to bf16 by convert_kernel
* decode kernel T == 1: memory-bound GEMV as one cooperative kernel (two phases, grid barrier)
L2 residency hints (evict_last bulk prefetch of the activation rows, persisting access-policy window) keep the
token rows and the output accumulator resident while the expert weights stream through.
"""
from __future__ import annotations
import os
import torch
import torch.nn as nn
from torch.utils.cpp_extension import load_inline
OP_TYPE = "glm52_fused_moe"
SUPPORTED_PRECISIONS = ["bf16"]
HARDWARE_REQUIRED = ["RTX_PRO_6000"]
os.environ.setdefault("TORCH_CUDA_ARCH_LIST", "12.0a")
_CUDA_SRC = r'''
// GLM-5.2 fused MoE (routed top-k + shared expert), SM120 mma.sync grouped GEMMs.
#include <torch/extension.h>
#include <ATen/cuda/CUDAContext.h>
#include <c10/cuda/CUDAGuard.h>
#include <cuda_runtime.h>
#include <cuda_bf16.h>
#include <stdint.h>
#include <algorithm>
#include <type_traits>
typedef __nv_bfloat16 bf16;
// ----------------------------------------------------------------------------------------------
// small PTX helpers
// ----------------------------------------------------------------------------------------------
__device__ __forceinline__ uint32_t smem_u32(const void* p) { return (uint32_t)__cvta_generic_to_shared(p); }
__device__ __forceinline__ uint64_t make_policy_last() {
uint64_t p;
asm volatile("createpolicy.fractional.L2::evict_last.b64 %0, 1.0;\n" : "=l"(p));
return p;
}
__device__ __forceinline__ void cp_async16(uint32_t dst, const void* src) {
asm volatile("cp.async.cg.shared.global.L2::128B [%0], [%1], 16;\n" ::"r"(dst), "l"(src));
}
__device__ __forceinline__ void cp_async16(uint32_t dst, const void* src, uint64_t pol) {
asm volatile("cp.async.cg.shared.global.L2::128B [%0], [%1], 16;\n" ::"r"(dst), "l"(src));
}
// bulk L2 prefetch of a contiguous range (TMA engine, evict_last policy): turns first-touch row misses into sequential
// DRAM reads that stay resident while the weights stream through
__device__ __forceinline__ void bulk_prefetch_l2_last(const void* p, uint32_t bytes, uint64_t pol) {
asm volatile("cp.async.bulk.prefetch.L2.global.L2::cache_hint [%0], %1, %2;\n" ::"l"(p), "r"(bytes), "l"(pol) : "memory");
}
__device__ __forceinline__ void cp_commit() { asm volatile("cp.async.commit_group;\n" ::); }
template <int N>
__device__ __forceinline__ void cp_wait() { asm volatile("cp.async.wait_group %0;\n" ::"n"(N)); }
__device__ __forceinline__ void ldsm_x4(uint32_t (&r)[4], uint32_t addr) {
asm volatile("ldmatrix.sync.aligned.m8n8.x4.shared.b16 {%0,%1,%2,%3}, [%4];\n"
: "=r"(r[0]), "=r"(r[1]), "=r"(r[2]), "=r"(r[3])
: "r"(addr));
}
__device__ __forceinline__ void mma_bf16(float (&c)[4], const uint32_t (&a)[4], uint32_t b0, uint32_t b1) {
asm volatile(
"mma.sync.aligned.m16n8k16.row.col.f32.bf16.bf16.f32 {%0,%1,%2,%3}, {%4,%5,%6,%7}, {%8,%9}, {%0,%1,%2,%3};\n"
: "+f"(c[0]), "+f"(c[1]), "+f"(c[2]), "+f"(c[3])
: "r"(a[0]), "r"(a[1]), "r"(a[2]), "r"(a[3]), "r"(b0), "r"(b1));
}
__device__ __forceinline__ uint32_t movmatrix_trans(uint32_t v) {
uint32_t r;
asm volatile("movmatrix.sync.aligned.m8n8.trans.b16 %0, %1;\n" : "=r"(r) : "r"(v));
return r;
}
__device__ __forceinline__ uint32_t pack_bf16(float lo, float hi) {
__nv_bfloat162 v = __floats2bfloat162_rn(lo, hi);
return *reinterpret_cast<uint32_t*>(&v);
}
__device__ __forceinline__ float silu_f(float x) { return __fdividef(x, 1.0f + __expf(-x)); }
// ----------------------------------------------------------------------------------------------
// routing prep: counting sort of (token, k) pairs by expert, plus tile tables for both GEMMs
// ----------------------------------------------------------------------------------------------
struct PrepParams {
const int64_t* ids; // [T, K]
const bf16* wts; // [T, K]
int T, K, E, NS;
int BM1, NB1, BM2, NB2;
int* tok_of_pos; // [(K+NS)*T]
int* dest_of_pos; // [(K+NS)*T]
float* w_of_pos; // [(K+NS)*T]
uint4* tiles1;
uint4* tiles2;
int* ntiles; // [2]
};
#define PREP_THREADS 1024
#define MAXEXP 1024
// Table index of a tile after interleaving shared-expert groups among the routed tiles.
// routed: r = routed tile index; shared: group j (one n-block of one shared expert), tile mt of the group.
__device__ __forceinline__ int interleave_index(bool routed, int r, int j, int mt, int Nr, int Ng, int ms) {
if (Ng == 0 || Nr == 0) return r;
if (routed) {
int cnt = min(Ng, (((2 * Ng * (r + 1) + Nr - 1) / Nr)) / 2);
return r + ms * cnt;
}
int Rj = ((2 * j + 1) * Nr) / (2 * Ng);
return Rj + j * ms + mt;
}
__global__ void __launch_bounds__(PREP_THREADS) prep_kernel(PrepParams p) {
__shared__ int s_cnt[MAXEXP];
__shared__ int s_off[MAXEXP];
__shared__ int s_cur[MAXEXP];
__shared__ int s_t1[MAXEXP + 1]; // start tile index per expert for gemm1
__shared__ int s_t2[MAXEXP + 1];
const int tid = threadIdx.x;
const int NX = p.E + p.NS;
const int TK = p.T * p.K;
for (int i = tid; i < MAXEXP; i += PREP_THREADS) s_cnt[i] = 0;
__syncthreads();
// histogram; 8 independent loads in flight per thread (this loop is latency bound)
for (int i0 = tid; i0 < TK; i0 += PREP_THREADS * 8) {
int ev[8];
#pragma unroll
for (int u = 0; u < 8; ++u) {
int i = i0 + u * PREP_THREADS;
ev[u] = (i < TK) ? (int)p.ids[i] : -1;
}
#pragma unroll
for (int u = 0; u < 8; ++u)
if (ev[u] >= 0) atomicAdd(&s_cnt[ev[u]], 1);
}
__syncthreads();
// exclusive scans (lane-chunked, one warp)
if (tid < 32) {
int per = (NX + 31) / 32;
int start = tid * per;
int end = min(start + per, NX);
int sum = 0, sum1 = 0, sum2 = 0;
for (int e = start; e < end; ++e) {
int n = (e < p.E) ? s_cnt[e] : p.T;
s_off[e] = sum;
s_t1[e] = sum1;
s_t2[e] = sum2;
sum += n;
sum1 += ((n + p.BM1 - 1) / p.BM1) * p.NB1;
sum2 += ((n + p.BM2 - 1) / p.BM2) * p.NB2;
}
int i0 = sum, i1 = sum1, i2 = sum2;
for (int d = 1; d < 32; d <<= 1) {
int v0 = __shfl_up_sync(0xffffffffu, i0, d);
int v1 = __shfl_up_sync(0xffffffffu, i1, d);
int v2 = __shfl_up_sync(0xffffffffu, i2, d);
if (tid >= d) { i0 += v0; i1 += v1; i2 += v2; }
}
int b0 = i0 - sum, b1 = i1 - sum1, b2 = i2 - sum2;
for (int e = start; e < end; ++e) { s_off[e] += b0; s_t1[e] += b1; s_t2[e] += b2; }
if (tid == 31) { p.ntiles[0] = i1; p.ntiles[1] = i2; }
}
__syncthreads();
for (int e = tid; e < NX; e += PREP_THREADS) s_cur[e] = s_off[e];
__syncthreads();
// scatter routed pairs (batched loads as above)
for (int i0 = tid; i0 < TK; i0 += PREP_THREADS * 8) {
int ev[8];
bf16 wv[8];
#pragma unroll
for (int u = 0; u < 8; ++u) {
int i = i0 + u * PREP_THREADS;
if (i < TK) { ev[u] = (int)p.ids[i]; wv[u] = p.wts[i]; } else { ev[u] = -1; }
}
#pragma unroll
for (int u = 0; u < 8; ++u) {
if (ev[u] >= 0) {
int i = i0 + u * PREP_THREADS;
int pos = atomicAdd(&s_cur[ev[u]], 1);
p.tok_of_pos[pos] = i / p.K;
p.dest_of_pos[pos] = i;
p.w_of_pos[pos] = __bfloat162float(wv[u]);
}
}
}
// shared experts: positions TK + s*T + t
for (int i = tid; i < p.NS * p.T; i += PREP_THREADS) {
int pos = TK + i;
p.tok_of_pos[pos] = i % p.T;
p.dest_of_pos[pos] = pos;
p.w_of_pos[pos] = 1.0f;
}
// fill tile tables, one warp per expert. Shared-expert tile groups (all m-tiles of one n-block, which
// share their weight tile through L2) are interleaved evenly among the routed tiles so that the
// compute-heavy shared work overlaps the DRAM-bound routed work instead of forming a tail.
const int warp = tid >> 5, lane = tid & 31;
const int Nr1 = s_t1[p.E], Nr2 = s_t2[p.E];
#define RIDX1 (s_t1[e] + li)
#define RIDX2 (s_t2[e] + li)
const int Ng1 = p.NS * p.NB1, Ng2 = p.NS * p.NB2;
const int ms1 = (p.T + p.BM1 - 1) / p.BM1, ms2 = (p.T + p.BM2 - 1) / p.BM2;
for (int e = warp; e < NX; e += PREP_THREADS / 32) {
int n = (e < p.E) ? s_cnt[e] : p.T;
if (n == 0) continue;
int start = s_off[e];
{
int m = (n + p.BM1 - 1) / p.BM1;
int tot = m * p.NB1;
// tile sizes are multiples of 32 (only the last tile is ragged): the two token groups of a CTA each take every
// other 16-token block, so an odd block count would leave one group idle for a whole block
int sz = (((n + m - 1) / m) + 31) & ~31;
if ((m - 1) * sz >= n) sz = (n + m - 1) / m;
for (int li = lane; li < tot; li += 32) {
int nblk = li / m, mt = li % m;
int lo = mt * sz, hi = min(n, lo + sz);
int idx = interleave_index(e < p.E, RIDX1, (e - p.E) * p.NB1 + nblk, mt, Nr1, Ng1, ms1);
p.tiles1[idx] = make_uint4(start + lo, hi - lo, nblk, e);
}
}
{
int m = (n + p.BM2 - 1) / p.BM2;
int tot = m * p.NB2;
int sz = (((n + m - 1) / m) + 31) & ~31;
if ((m - 1) * sz >= n) sz = (n + m - 1) / m;
for (int li = lane; li < tot; li += 32) {
int nblk = li / m, mt = li % m;
int lo = mt * sz, hi = min(n, lo + sz);
int idx = interleave_index(e < p.E, RIDX2, (e - p.E) * p.NB2 + nblk, mt, Nr2, Ng2, ms2);
p.tiles2[idx] = make_uint4(start + lo, hi - lo, nblk, e);
}
}
}
}
// ----------------------------------------------------------------------------------------------
// grouped GEMM (swap-AB: weights are the MMA "A" operand, tokens the "B" operand)
// GATEUP=true : GEMM1: rows = WROWS (WROWS/2 gate rows then WROWS/2 up rows),
// epilogue = silu(gate)*up -> h (bf16)
// GATEUP=false: GEMM2: rows = WROWS output columns, epilogue = weight * acc -> Y (bf16)
// The number of active 16-token blocks per warp is a warp-uniform runtime value; the code is
// specialised per value (switch) so the hot loop has no predicates or branches.
// ----------------------------------------------------------------------------------------------
template <bool GATEUP, int BM, int WROWS, int WT, int WW, int BK, int STAGES>
struct GemmCfg {
static constexpr int NTHREADS = WT * WW * 32;
static constexpr int ROWB = BK * 2;
static constexpr int CPR = BK / 8; // 16B chunks per row
static constexpr int W_BYTES = WROWS * ROWB;
static constexpr int X_BYTES = BM * ROWB;
static constexpr int STAGE_BYTES = W_BYTES + X_BYTES;
static constexpr int MI = WROWS / (WW * 16);
static constexpr int NB16 = BM / (16 * WT);
static constexpr int K16 = BK / 16;
static constexpr int W_ITERS = (WROWS * CPR) / NTHREADS;
static constexpr int X_ITERS = (BM * CPR) / NTHREADS;
static constexpr int META_OFF = STAGES * STAGE_BYTES;
static constexpr int SMEM_BYTES = META_OFF + BM * 8; // stages + per-token (int, float) table
static_assert(WROWS % (WW * 16) == 0, "rows per warp group must be a multiple of 16");
static_assert(BM % (16 * WT) == 0, "BM must divide into 16-token blocks per token group");
static_assert((WROWS * CPR) % NTHREADS == 0, "W chunks must divide across threads");
static_assert((BM * CPR) % NTHREADS == 0, "X chunks must divide across threads");
static_assert(BK == 64 || BK == 32, "swizzle supports 128B or 64B rows");
static_assert(NTHREADS % CPR == 0, "");
static_assert(K16 % 2 == 0, "");
static_assert(NB16 <= 8, "extend the dispatch switch");
};
template <int BK>
__device__ __forceinline__ int swz_of_row(int row) {
return BK == 64 ? (row & 7) : ((row >> 1) & 3);
}
template <bool GATEUP, int BM, int WROWS, int WT, int WW, int BK, int STAGES, int MINB, bool ATOMIC_OUT = false>
__global__ void __launch_bounds__(GemmCfg<GATEUP, BM, WROWS, WT, WW, BK, STAGES>::NTHREADS, MINB)
moe_gemm_kernel(const bf16* __restrict__ xin, // GEMM1: x [T,H]; GEMM2: h_buf [rows, I]
const bf16* __restrict__ w_routed, // [E, rows_total, K]
const bf16* __restrict__ w_shared, // [NS, rows_total, K]
const int* __restrict__ meta_of_pos, // GEMM1: token index; GEMM2: Y row (dest)
const float* __restrict__ w_of_pos, // GEMM2 only
const uint4* __restrict__ tiles, const int* __restrict__ ntiles,
bf16* __restrict__ outbuf, // GEMM1: h_buf [rows, I]; GEMM2: Y [rows, H]
int K, // reduction length (H for GEMM1, I for GEMM2)
int rows_total, // 2*I for GEMM1, H for GEMM2
int out_ld, // GEMM1: I ; GEMM2: H
int E, int xrows, uint64_t pol_w_arg, uint64_t pol_x_arg, int nb_lo, int nb_hi) {
using C = GemmCfg<GATEUP, BM, WROWS, WT, WW, BK, STAGES>;
constexpr int NT = C::NTHREADS;
constexpr int MI = C::MI, NB16 = C::NB16, K16 = C::K16;
constexpr int CPR = C::CPR;
extern __shared__ __align__(128) uint8_t smem[];
const int tile = blockIdx.x;
const uint4 ti = tiles[tile]; // both loads in flight together; entries past ntiles are never used
if (tile >= ntiles[0]) return;
const int pos0 = (int)ti.x, nvalid = (int)ti.y, nblk = (int)ti.z, ex = (int)ti.w;
if (nblk < nb_lo || nblk >= nb_hi) return; // column-split passes of the atomic output path
const int tid = threadIdx.x, lane = tid & 31, warp = tid >> 5;
const int wt = warp % WT, ww = warp / WT;
int* s_meta = reinterpret_cast<int*>(smem + C::META_OFF);
float* s_w = reinterpret_cast<float*>(smem + C::META_OFF + BM * 4);
const uint32_t smem_base = smem_u32(smem);
const uint64_t pol_w = 0;
const uint64_t pol_x = 0;
const bf16* Wbase = (ex < E) ? (w_routed + (size_t)ex * rows_total * K) : (w_shared + (size_t)(ex - E) * rows_total * K);
const int row_base = nblk * (GATEUP ? (WROWS / 2) : WROWS); // first i (GEMM1) / n (GEMM2) of this tile
const int k_steps = K / BK;
// ---- per-thread cp.async assignment ----
const int ch = tid % CPR;
const int rsub = tid / CPR; // base row for this thread; rows advance by NT/CPR
constexpr int RSTEP = NT / CPR;
uint32_t w_soff[C::W_ITERS];
uint32_t w_goff[C::W_ITERS]; // element offsets from Wbase
#pragma unroll
for (int j = 0; j < C::W_ITERS; ++j) {
int row = rsub + RSTEP * j;
w_soff[j] = row * C::ROWB + ((ch ^ swz_of_row<BK>(row)) << 4);
int grow;
if (GATEUP) grow = (row < WROWS / 2) ? (row_base + row) : ((rows_total / 2) + row_base + (row - WROWS / 2));
else grow = row_base + row;
w_goff[j] = (uint32_t)grow * (uint32_t)K + ch * 8;
}
auto kmap = [&](int ks) { return ks; };
auto load_w = [&](uint32_t stage_addr, int ks) {
#pragma unroll
for (int j = 0; j < C::W_ITERS; ++j) cp_async16(stage_addr + w_soff[j], Wbase + (w_goff[j] + kmap(ks) * BK), pol_w);
};
uint32_t x_soff[C::X_ITERS];
uint32_t x_goff[C::X_ITERS];
bool x_ok[C::X_ITERS];
auto load_x = [&](uint32_t stage_addr, int ks) {
#pragma unroll
for (int j = 0; j < C::X_ITERS; ++j)
if (x_ok[j]) cp_async16(stage_addr + C::W_BYTES + x_soff[j], xin + (x_goff[j] + kmap(ks) * BK));
};
// prologue: weight loads do not depend on the routing metadata, issue them first;
// then fetch the metadata (its latency overlaps the weight loads in flight) and issue the token loads.
#pragma unroll
for (int s = 0; s < STAGES - 1; ++s) load_w(smem_base + s * C::STAGE_BYTES, s);
if (tid < BM) {
int m = 0;
float w = 0.f;
if (tid < nvalid) {
m = meta_of_pos[pos0 + tid];
if (!GATEUP) w = w_of_pos[pos0 + tid];
}
s_meta[tid] = m;
if (!GATEUP) s_w[tid] = w;
}
__syncthreads();
{
const uint64_t pol_last = make_policy_last();
// each tile-row is prefetched (whole row, one bulk op) by exactly one of the CTAs that share this token tile
const int nb_tiles = rows_total / WROWS;
for (int r = tid; r < BM; r += NT)
if (r < nvalid && (r % nb_tiles) == nblk) {
const size_t xr = GATEUP ? (size_t)s_meta[r] : (size_t)(pos0 + r);
bulk_prefetch_l2_last(xin + xr * K, (uint32_t)K * 2, pol_last);
}
}
#pragma unroll
for (int j = 0; j < C::X_ITERS; ++j) {
int row = rsub + RSTEP * j;
x_soff[j] = row * C::ROWB + ((ch ^ swz_of_row<BK>(row)) << 4);
x_ok[j] = row < nvalid;
uint32_t xrow = GATEUP ? (uint32_t)s_meta[row] : (uint32_t)(pos0 + row);
x_goff[j] = xrow * (uint32_t)K + ch * 8;
}
load_x(smem_base, 0);
cp_commit();
#pragma unroll
for (int s = 1; s < STAGES - 1; ++s) {
load_x(smem_base + s * C::STAGE_BYTES, s);
cp_commit();
}
// ---- fragment addressing ----
const int lr = lane & 7; // row within 8x8 matrix
const int lm = lane >> 3; // matrix index 0..3
uint32_t a_row_off[MI];
#pragma unroll
for (int mi = 0; mi < MI; ++mi) {
int row;
if (GATEUP) row = ((lm & 1) ? (WROWS / 2) : 0) + ww * MI * 8 + mi * 8 + lr;
else row = ww * MI * 16 + mi * 16 + (lm & 1) * 8 + lr;
a_row_off[mi] = row * C::ROWB;
}
const int a_chunk_hi = lane >> 4;
const int swz = swz_of_row<BK>(lr); // rows are 8-aligned so row&7 == lr
uint32_t b_row_off[NB16];
#pragma unroll
for (int jb = 0; jb < NB16; ++jb) {
int blk = wt + WT * jb;
int row = blk * 16 + (lm >> 1) * 8 + lr;
b_row_off[jb] = C::W_BYTES + row * C::ROWB;
}
const int b_chunk_lo = lm & 1;
// number of active 16-token blocks for this warp
const int nblocks = (nvalid + 15) >> 4;
int nact = (nblocks - wt + WT - 1) / WT;
nact = nact < 0 ? 0 : nact;
const int g = lane >> 2, q = lane & 3;
auto body = [&](auto nact_c) {
constexpr int NACT = decltype(nact_c)::value;
constexpr int NJ = 2 * NACT;
float acc[MI][(NJ > 0 ? NJ : 1)][4];
#pragma unroll
for (int mi = 0; mi < MI; ++mi)
#pragma unroll
for (int nj = 0; nj < (NJ > 0 ? NJ : 1); ++nj)
#pragma unroll
for (int r = 0; r < 4; ++r) acc[mi][nj][r] = 0.f;
uint32_t afr[2][MI][4];
uint32_t bfr[2][(NACT > 0 ? NACT : 1)][4];
auto ld_frags = [&](int buf, uint32_t sbase, int kk) {
if constexpr (NACT > 0) {
const uint32_t a_col = (uint32_t)(((2 * kk + a_chunk_hi) ^ swz) << 4);
const uint32_t b_col = (uint32_t)(((2 * kk + b_chunk_lo) ^ swz) << 4);
#pragma unroll
for (int mi = 0; mi < MI; ++mi) ldsm_x4(afr[buf][mi], sbase + a_row_off[mi] + a_col);
#pragma unroll
for (int jb = 0; jb < NACT; ++jb) ldsm_x4(bfr[buf][jb], sbase + b_row_off[jb] + b_col);
}
};
auto do_mma = [&](int buf) {
if constexpr (NACT > 0) {
#pragma unroll
for (int jb = 0; jb < NACT; ++jb)
#pragma unroll
for (int mi = 0; mi < MI; ++mi) {
mma_bf16(acc[mi][2 * jb], afr[buf][mi], bfr[buf][jb][0], bfr[buf][jb][1]);
mma_bf16(acc[mi][2 * jb + 1], afr[buf][mi], bfr[buf][jb][2], bfr[buf][jb][3]);
}
}
};
cp_wait<STAGES - 2>();
__syncthreads();
ld_frags(0, smem_base, 0);
int st_cur = 0;
int st_nxt = 1 % STAGES;
int st_ld = (STAGES - 1) % STAGES;
for (int ks = 0; ks < k_steps; ++ks) {
const uint32_t sb_cur = smem_base + st_cur * C::STAGE_BYTES;
const uint32_t sb_nxt = smem_base + st_nxt * C::STAGE_BYTES;
#pragma unroll
for (int kk = 0; kk < K16; ++kk) {
const int cur = kk & 1, nxt = cur ^ 1;
if (kk == K16 - 1) {
cp_wait<STAGES - 2>();
__syncthreads();
ld_frags(nxt, sb_nxt, 0);
} else {
ld_frags(nxt, sb_cur, kk + 1);
}
if (kk == 0) {
if (ks + STAGES - 1 < k_steps) {
const uint32_t sb_ld = smem_base + st_ld * C::STAGE_BYTES;
load_w(sb_ld, ks + STAGES - 1);
load_x(sb_ld, ks + STAGES - 1);
}
cp_commit();
}
do_mma(cur);
}
st_cur = st_nxt;
st_nxt = (st_nxt + 1 == STAGES) ? 0 : st_nxt + 1;
st_ld = (st_ld + 1 == STAGES) ? 0 : st_ld + 1;
}
cp_wait<0>();
// ---- epilogue ----
if constexpr (NACT > 0) {
#pragma unroll
for (int jb = 0; jb < NACT; ++jb) {
const int blk = wt + WT * jb;
#pragma unroll
for (int half = 0; half < 2; ++half) {
const int nj = 2 * jb + half;
const int tok_pair0 = blk * 16 + half * 8 + 2 * q;
const int trow = blk * 16 + half * 8 + g;
if (GATEUP) {
#pragma unroll
for (int mi = 0; mi < MI; ++mi) {
float h0 = silu_f(acc[mi][nj][0]) * acc[mi][nj][2];
float h1 = silu_f(acc[mi][nj][1]) * acc[mi][nj][3];
uint32_t v = movmatrix_trans(pack_bf16(h0, h1));
if (trow < nvalid) {
int icol = row_base + ww * MI * 8 + mi * 8 + 2 * q;
*reinterpret_cast<uint32_t*>(outbuf + (size_t)(pos0 + trow) * out_ld + icol) = v;
}
}
} else if constexpr (ATOMIC_OUT) {
// accumulate the routing-weighted result straight into the fp32 output (kept resident in L2)
float* oacc = reinterpret_cast<float*>(outbuf);
const int col_off = nb_lo * WROWS;
const bool va = tok_pair0 < nvalid, vb = tok_pair0 + 1 < nvalid;
const float wa = va ? s_w[tok_pair0] : 0.f, wb = vb ? s_w[tok_pair0 + 1] : 0.f;
const int ta = va ? s_meta[tok_pair0] : 0, tb = vb ? s_meta[tok_pair0 + 1] : 0;
#pragma unroll
for (int mi = 0; mi < MI; ++mi) {
const int nrow = row_base + ww * MI * 16 + mi * 16 + g;
if (va) {
atomicAdd(oacc + (size_t)ta * out_ld + nrow - col_off, acc[mi][nj][0] * wa);
atomicAdd(oacc + (size_t)ta * out_ld + nrow - col_off + 8, acc[mi][nj][2] * wa);
}
if (vb) {
atomicAdd(oacc + (size_t)tb * out_ld + nrow - col_off, acc[mi][nj][1] * wb);
atomicAdd(oacc + (size_t)tb * out_ld + nrow - col_off + 8, acc[mi][nj][3] * wb);
}
}
} else {
float w0 = (tok_pair0 < nvalid) ? s_w[tok_pair0] : 0.f;
float w1 = (tok_pair0 + 1 < nvalid) ? s_w[tok_pair0 + 1] : 0.f;
int dest = (trow < nvalid) ? s_meta[trow] : 0;
#pragma unroll
for (int mi = 0; mi < MI; ++mi) {
uint32_t v0 = movmatrix_trans(pack_bf16(acc[mi][nj][0] * w0, acc[mi][nj][1] * w1));
uint32_t v1 = movmatrix_trans(pack_bf16(acc[mi][nj][2] * w0, acc[mi][nj][3] * w1));
if (trow < nvalid) {
int ncol = row_base + ww * MI * 16 + mi * 16 + 2 * q;
bf16* dst = outbuf + (size_t)dest * out_ld + ncol;
*reinterpret_cast<uint32_t*>(dst) = v0;
*reinterpret_cast<uint32_t*>(dst + 8) = v1;
}
}
}
}
}
}
};
// warp-uniform dispatch on the number of active token blocks
switch (nact) {
case 0: body(std::integral_constant<int, 0>{}); break;
case 1: body(std::integral_constant<int, 1>{}); break;
case 2: if constexpr (NB16 >= 2) body(std::integral_constant<int, 2>{}); break;
case 3: if constexpr (NB16 >= 3) body(std::integral_constant<int, 3>{}); break;
case 4: if constexpr (NB16 >= 4) body(std::integral_constant<int, 4>{}); break;
case 5: if constexpr (NB16 >= 5) body(std::integral_constant<int, 5>{}); break;
case 6: if constexpr (NB16 >= 6) body(std::integral_constant<int, 6>{}); break;
case 7: if constexpr (NB16 >= 7) body(std::integral_constant<int, 7>{}); break;
default: if constexpr (NB16 >= 8) body(std::integral_constant<int, 8>{}); break;
}
}
// ----------------------------------------------------------------------------------------------
// combine: out[t] = sum_k Y[t*K+k] + sum_s Ys[T*K + s*T + t]
// ----------------------------------------------------------------------------------------------
__global__ void __launch_bounds__(256) combine_kernel(const bf16* __restrict__ Y, bf16* __restrict__ out, int T, int K,
int NS, int H) {
const int t = blockIdx.x;
const int n = (blockIdx.y * blockDim.x + threadIdx.x) * 8;
if (n >= H) return;
float acc[8];
#pragma unroll
for (int i = 0; i < 8; ++i) acc[i] = 0.f;
auto add_row = [&](size_t row) {
uint4 v = *reinterpret_cast<const uint4*>(Y + row * H + n);
const bf16* pv = reinterpret_cast<const bf16*>(&v);
#pragma unroll
for (int i = 0; i < 8; ++i) acc[i] += __bfloat162float(pv[i]);
};
for (int k = 0; k < K; ++k) add_row((size_t)t * K + k);
for (int s = 0; s < NS; ++s) add_row((size_t)T * K + (size_t)s * T + t);
uint4 o;
o.x = pack_bf16(acc[0], acc[1]);
o.y = pack_bf16(acc[2], acc[3]);
o.z = pack_bf16(acc[4], acc[5]);
o.w = pack_bf16(acc[6], acc[7]);
*reinterpret_cast<uint4*>(out + (size_t)t * H + n) = o;
}
// ----------------------------------------------------------------------------------------------
// host side
// ----------------------------------------------------------------------------------------------
// fp32 accumulator -> bf16 output, and re-zero the accumulator for the next call
__global__ void __launch_bounds__(256) convert_kernel(float* __restrict__ acc, bf16* __restrict__ out, int T, int Hc,
int H, int col0) {
const size_t n4 = (size_t)T * Hc / 4;
const int c4n = Hc / 4;
for (size_t i = blockIdx.x * (size_t)blockDim.x + threadIdx.x; i < n4; i += (size_t)gridDim.x * blockDim.x) {
float4 v = __ldcg(reinterpret_cast<const float4*>(acc) + i);
uint2 o;
o.x = pack_bf16(v.x, v.y);
o.y = pack_bf16(v.z, v.w);
const size_t row = i / c4n, c4 = i - row * c4n;
*reinterpret_cast<uint2*>(out + row * H + col0 + c4 * 4) = o;
__stcg(reinterpret_cast<float4*>(acc) + i, make_float4(0.f, 0.f, 0.f, 0.f));
}
}
// persistent fp32 output accumulator (invariant: all zeros between calls)
static torch::Tensor& oacc_for(int dev, int64_t numel, const at::TensorOptions& opts) {
static torch::Tensor bufs[16];
if (!bufs[dev].defined() || bufs[dev].numel() < numel) bufs[dev] = torch::zeros({numel}, opts.dtype(torch::kFloat32));
return bufs[dev];
}
template <bool GATEUP, int BM, int WROWS, int WT, int WW, int BK, int STAGES, int MINB, bool ATOMIC_OUT = false>
static void launch_gemm(const bf16* xin, const bf16* wr, const bf16* ws, const int* meta, const float* wpos,
const uint4* tiles, const int* ntiles, bf16* outbuf, int K, int rows_total, int out_ld, int E,
int xrows, int max_tiles, cudaStream_t stream, const void* win_base = nullptr,
size_t win_bytes = 0, int nb_lo = 0, int nb_hi = 0x7fffffff) {
using C = GemmCfg<GATEUP, BM, WROWS, WT, WW, BK, STAGES>;
auto kern = moe_gemm_kernel<GATEUP, BM, WROWS, WT, WW, BK, STAGES, MINB, ATOMIC_OUT>;
static bool init = false;
if (!init) {
cudaFuncSetAttribute(kern, cudaFuncAttributeMaxDynamicSharedMemorySize, C::SMEM_BYTES);
init = true;
}
if (win_bytes > 0) {
// per-launch L2 access-policy window: keep this range resident (persisting) against the weight stream
cudaLaunchConfig_t cfg = {};
cfg.gridDim = dim3(max_tiles);
cfg.blockDim = dim3(C::NTHREADS);
cfg.dynamicSmemBytes = C::SMEM_BYTES;
cfg.stream = stream;
cudaLaunchAttribute attr[1];
attr[0].id = cudaLaunchAttributeAccessPolicyWindow;
attr[0].val.accessPolicyWindow.base_ptr = const_cast<void*>(win_base);
attr[0].val.accessPolicyWindow.num_bytes = win_bytes;
attr[0].val.accessPolicyWindow.hitRatio = 1.0f;
attr[0].val.accessPolicyWindow.hitProp = cudaAccessPropertyPersisting;
attr[0].val.accessPolicyWindow.missProp = cudaAccessPropertyStreaming;
cfg.attrs = attr;
cfg.numAttrs = 1;
cudaLaunchKernelEx(&cfg, kern, xin, wr, ws, meta, wpos, tiles, ntiles, outbuf, K, rows_total, out_ld, E, xrows,
0x12F0000000000000ull, 0x14F0000000000000ull, nb_lo, nb_hi);
return;
}
kern<<<max_tiles, C::NTHREADS, C::SMEM_BYTES, stream>>>(xin, wr, ws, meta, wpos, tiles, ntiles, outbuf, K, rows_total,
out_ld, E, xrows, 0x12F0000000000000ull, 0x14F0000000000000ull, nb_lo,
nb_hi);
}
template <int BM1, int WR1, int WT1, int WW1, int BK1, int ST1, int BM2, int WR2, int WT2, int WW2, int BK2, int ST2,
int MB1 = 1, int MB2 = 1>
static torch::Tensor run_pipeline(torch::Tensor x, torch::Tensor ids, torch::Tensor wts, torch::Tensor w1r,
torch::Tensor w2r, torch::Tensor w1s, torch::Tensor w2s) {
TORCH_CHECK(x.is_cuda() && x.dtype() == torch::kBFloat16 && x.is_contiguous(), "x must be contiguous bf16 cuda");
const int T = (int)x.size(0), H = (int)x.size(1);
const int K = (int)ids.size(1);
const int E = (int)w1r.size(0);
const int I = (int)(w1r.size(1) / 2);
const int NS = (int)w1s.size(0);
TORCH_CHECK(H % BK1 == 0 && I % BK2 == 0 && I % (WR1 / 2) == 0 && H % WR2 == 0 && (E + NS) <= MAXEXP, "unsupported dims");
c10::cuda::CUDAGuard guard(x.device());
cudaStream_t stream = at::cuda::getCurrentCUDAStream();
const int NB1 = I / (WR1 / 2); // gemm1 tiles along I
const int NB2 = H / WR2; // gemm2 tiles along H
const int R = (K + NS) * T; // total positions
const int mt_bound1 = E + (K * T + BM1 - 1) / BM1 + NS * ((T + BM1 - 1) / BM1);
const int mt_bound2 = E + (K * T + BM2 - 1) / BM2 + NS * ((T + BM2 - 1) / BM2);
const int max_t1 = mt_bound1 * NB1, max_t2 = mt_bound2 * NB2;
// one scratch allocation for everything (host overhead matters at small T)
size_t off = 0;
auto carve = [&](size_t bytes) { size_t o = off; off += (bytes + 255) & ~(size_t)255; return o; };
const size_t o_tok = carve((size_t)R * 4), o_dest = carve((size_t)R * 4), o_wpos = carve((size_t)R * 4);
const size_t o_t1 = carve((size_t)max_t1 * 16), o_t2 = carve((size_t)max_t2 * 16), o_nt = carve(256);
// atomic output path: fp32 accumulator kept L2-resident; column-split into passes so that it fits the persisting region
int nsplit = 0;
for (int s = 1; s <= 4; s *= 2)
if (NB2 % s == 0 && (size_t)T * (H / s) * 4 <= ((size_t)80 << 20)) { nsplit = s; break; }
const bool use_atomic = nsplit > 0;
const size_t o_h = carve((size_t)R * I * 2), o_y = carve(use_atomic ? 256 : (size_t)R * H * 2);
auto scratch = torch::empty({(int64_t)off}, x.options().dtype(torch::kUInt8));
auto out = torch::empty({T, H}, x.options());
uint8_t* sp = scratch.data_ptr<uint8_t>();
int* tok = reinterpret_cast<int*>(sp + o_tok);
int* dest = reinterpret_cast<int*>(sp + o_dest);
float* wpos = reinterpret_cast<float*>(sp + o_wpos);
int* ntiles = reinterpret_cast<int*>(sp + o_nt);
PrepParams pp;
pp.ids = ids.data_ptr<int64_t>();
pp.wts = reinterpret_cast<const bf16*>(wts.data_ptr());
pp.T = T; pp.K = K; pp.E = E; pp.NS = NS;
pp.BM1 = BM1; pp.NB1 = NB1; pp.BM2 = BM2; pp.NB2 = NB2;
pp.tok_of_pos = tok;
pp.dest_of_pos = dest;
pp.w_of_pos = wpos;
pp.tiles1 = reinterpret_cast<uint4*>(sp + o_t1);
pp.tiles2 = reinterpret_cast<uint4*>(sp + o_t2);
pp.ntiles = ntiles;
prep_kernel<<<1, PREP_THREADS, 0, stream>>>(pp);
const bf16* xp = reinterpret_cast<const bf16*>(x.data_ptr());
const bf16* w1rp = reinterpret_cast<const bf16*>(w1r.data_ptr());
const bf16* w2rp = reinterpret_cast<const bf16*>(w2r.data_ptr());
const bf16* w1sp = reinterpret_cast<const bf16*>(w1s.data_ptr());
const bf16* w2sp = reinterpret_cast<const bf16*>(w2s.data_ptr());
bf16* hp = reinterpret_cast<bf16*>(sp + o_h);
bf16* yp = reinterpret_cast<bf16*>(sp + o_y);
launch_gemm<true, BM1, WR1, WT1, WW1, BK1, ST1, MB1>(xp, w1rp, w1sp, tok, nullptr, pp.tiles1, ntiles, hp, H, 2 * I, I, E,
T, max_t1, stream);
if (use_atomic) {
const int Hc = H / nsplit;
float* oacc = oacc_for(x.get_device(), (int64_t)T * Hc, x.options()).data_ptr<float>();
const size_t obytes = (size_t)T * Hc * 4;
for (int s = 0; s < nsplit; ++s) {
launch_gemm<false, BM2, WR2, WT2, WW2, BK2, ST2, MB2, true>(
hp, w2rp, w2sp, tok, wpos, pp.tiles2, ntiles + 1, reinterpret_cast<bf16*>(oacc), I, H, Hc, E, R, max_t2, stream,
oacc, std::min<size_t>(obytes, (size_t)128 << 20), s * (NB2 / nsplit), (s + 1) * (NB2 / nsplit));
convert_kernel<<<(unsigned)std::min<size_t>(obytes / 16 / 256 + 1, 188 * 8), 256, 0, stream>>>(
oacc, reinterpret_cast<bf16*>(out.data_ptr()), T, Hc, H, s * Hc);
}
cudaCtxResetPersistingL2Cache(); // demotes the persisting lines once the queued work has run: nothing stays pinned in L2 after the call
return out;
}
launch_gemm<false, BM2, WR2, WT2, WW2, BK2, ST2, MB2>(hp, w2rp, w2sp, dest, wpos, pp.tiles2, ntiles + 1, yp, I, H, H, E,
R, max_t2, stream);
dim3 cg(T, (H / 8 + 255) / 256);
combine_kernel<<<cg, 256, 0, stream>>>(yp, reinterpret_cast<bf16*>(out.data_ptr()), T, K, NS, H);
cudaCtxResetPersistingL2Cache(); // demotes the evict_last activation rows once the queued work has run
return out;
}
static void maybe_set_persist() {
// Reserve the device maximum (capped at 80 MB) of L2 for persisting (evict_last / access-policy-window) lines.
static bool done[16] = {false};
int dev = 0;
cudaGetDevice(&dev);
if (dev < 0 || dev >= 16 || done[dev]) return;
done[dev] = true;
size_t want = (size_t)80 << 20;
int maxp = 0;
if (cudaDeviceGetAttribute(&maxp, cudaDevAttrMaxPersistingL2CacheSize, dev) == cudaSuccess && (size_t)maxp < want)
want = (size_t)maxp;
cudaDeviceSetLimit(cudaLimitPersistingL2CacheSize, want); // best effort: the hints are optional
}
static torch::Tensor moe_forward(torch::Tensor x, torch::Tensor ids, torch::Tensor wts, torch::Tensor w1r,
torch::Tensor w2r, torch::Tensor w1s, torch::Tensor w2s) {
maybe_set_persist();
// GEMM1: BM WROWS WT WW BK STAGES GEMM2: BM WROWS WT WW BK STAGES
return run_pipeline<128, 256, 2, 4, 32, 4, 128, 256, 2, 4, 32, 4>(x, ids, wts, w1r, w2r, w1s, w2s);
}
// ----------------------------------------------------------------------------------------------
// T == 1 decode helpers (CUDA-core GEMV on streamed weights)
// ----------------------------------------------------------------------------------------------
__device__ __forceinline__ uint4 ldg_stream(const void* p, uint64_t) {
uint4 v;
asm volatile("ld.global.nc.L1::no_allocate.L2::128B.v4.u32 {%0,%1,%2,%3}, [%4];\n"
: "=r"(v.x), "=r"(v.y), "=r"(v.z), "=r"(v.w)
: "l"(p));
return v;
}
__device__ __forceinline__ float bf_lo(uint32_t u) { return __uint_as_float(u << 16); }
__device__ __forceinline__ float bf_hi(uint32_t u) { return __uint_as_float(u & 0xffff0000u); }
__device__ __forceinline__ float dot8_bf16(const uint4& w, const uint4& xv) {
float a = bf_lo(w.x) * bf_lo(xv.x);
a = fmaf(bf_hi(w.x), bf_hi(xv.x), a);
a = fmaf(bf_lo(w.y), bf_lo(xv.y), a);
a = fmaf(bf_hi(w.y), bf_hi(xv.y), a);
a = fmaf(bf_lo(w.z), bf_lo(xv.z), a);
a = fmaf(bf_hi(w.z), bf_hi(xv.z), a);
a = fmaf(bf_lo(w.w), bf_lo(xv.w), a);
a = fmaf(bf_hi(w.w), bf_hi(xv.w), a);
return a;
}
// ----------------------------------------------------------------------------------------------
// T == 1 decode path, v2: two phases separated by a grid barrier (all CTAs co-resident, cooperative launch).
// phase 1: h[slot][i] = silu(x . Wg[i]) * (x . Wu[i]) flattened over (slot, i), contiguous weight rows per CTA
// phase 2: y[slot][n] = W2[n, :] . h[slot] flattened over (slot, n), full contiguous 4 KB rows
// out[n] += w_slot * y[slot][n] (fp32 atomics, only NSLOT per output element)
// The down-projection weights of a CTA's first rows are loaded into registers before the barrier wait, so DRAM
// stays busy while the slowest CTA finishes phase 1. The last CTA converts to bf16 and re-zeroes the scratch.
// ----------------------------------------------------------------------------------------------
__global__ void __launch_bounds__(256, 1)
decode2_kernel(const bf16* __restrict__ x, const int64_t* __restrict__ ids, const bf16* __restrict__ wts,
const bf16* __restrict__ w1r, const bf16* __restrict__ w2r, const bf16* __restrict__ w1s,
const bf16* __restrict__ w2s, float* __restrict__ hbuf, float* __restrict__ acc,
unsigned* __restrict__ bar, unsigned* __restrict__ done, bf16* __restrict__ out, int K, int NSLOT,
int H, int I) {
extern __shared__ __align__(16) uint8_t dsm[];
bf16* s_x = reinterpret_cast<bf16*>(dsm); // [H]
float* s_h = reinterpret_cast<float*>(dsm + H * 2); // [2][I]
__shared__ int s_last;
const int tid = threadIdx.x, lane = tid & 31, warp = tid >> 5;
const int nb = gridDim.x, c = blockIdx.x;
auto w1_of = [&](int slot) -> const bf16* {
if (slot < K) return w1r + (size_t)(int)ids[slot] * 2 * (size_t)I * H;
return w1s + (size_t)(slot - K) * 2 * (size_t)I * H;
};
auto w2_of = [&](int slot) -> const bf16* {
if (slot < K) return w2r + (size_t)(int)ids[slot] * (size_t)H * I;
return w2s + (size_t)(slot - K) * (size_t)H * I;
};
for (int cc = tid; cc < H / 8; cc += 256)
reinterpret_cast<uint4*>(s_x)[cc] = reinterpret_cast<const uint4*>(x)[cc];
__syncthreads();
// ---------------- phase 1 ----------------
{
const long long P = (long long)NSLOT * I;
const int p0 = (int)((long long)c * P / nb), p1 = (int)((long long)(c + 1) * P / nb);
const int NCH = H / 256;
for (int p = p0 + warp; p < p1; p += 8) {
const int slot = p / I, i = p - slot * I;
const bf16* W1 = w1_of(slot);
const bf16* rg = W1 + (size_t)i * H;
const bf16* ru = W1 + (size_t)(I + i) * H;
float g = 0.f, u = 0.f;
#pragma unroll 1
for (int cb = 0; cb < NCH; cb += 8) {
uint4 wg[8], wu[8];
#pragma unroll
for (int t = 0; t < 8; ++t) {
wg[t] = ldg_stream(rg + (size_t)((cb + t) * 32 + lane) * 8, 0);
wu[t] = ldg_stream(ru + (size_t)((cb + t) * 32 + lane) * 8, 0);
}
#pragma unroll
for (int t = 0; t < 8; ++t) {
const uint4 xv = *reinterpret_cast<const uint4*>(s_x + ((cb + t) * 32 + lane) * 8);
g += dot8_bf16(wg[t], xv);
u += dot8_bf16(wu[t], xv);
}
}
#pragma unroll
for (int o = 16; o > 0; o >>= 1) {
g += __shfl_xor_sync(0xffffffffu, g, o);
u += __shfl_xor_sync(0xffffffffu, u, o);
}
if (lane == 0) hbuf[p] = silu_f(g) * u;
}
}
// ---------------- phase 2 setup: row range and first batch of weight rows (independent of h) ----------------
const long long Q = (long long)NSLOT * H;
const int q0 = (int)((long long)c * Q / nb), q1 = (int)((long long)(c + 1) * Q / nb);
const int slot_a = q0 / H;
const int NCH2 = I / 256; // 16 B chunks per lane per row (I = 2048 -> 8)
constexpr int RB = 2; // rows per warp per batch
uint4 cur[RB][8]; // requires NCH2 == 8
auto load_rows = [&](uint4 (&dst)[RB][8], int r0) {
#pragma unroll
for (int b = 0; b < RB; ++b) {
const int r = r0 + b * 8;
if (r < q1) {
const int slot = r / H, n = r - slot * H;
const bf16* row = w2_of(slot) + (size_t)n * I;
#pragma unroll
for (int t = 0; t < 8; ++t) dst[b][t] = ldg_stream(row + (size_t)(t * 32 + lane) * 8, 0);
}
}
};
load_rows(cur, q0 + warp);
// ---------------- grid barrier ----------------
__threadfence();
__syncthreads();
if (tid == 0) {
atomicAdd(bar, 1u);
while (*reinterpret_cast<volatile unsigned*>(bar) < (unsigned)nb) __nanosleep(32);
__threadfence();
}
__syncthreads();
// h vectors of the (at most two) slots this CTA touches
const int slot_b = (q1 - 1) / H;
for (int k = tid; k < I; k += 256) {
s_h[k] = __ldcg(hbuf + (size_t)slot_a * I + k);
if (slot_b != slot_a) s_h[I + k] = __ldcg(hbuf + (size_t)slot_b * I + k);
}
__syncthreads();
// ---------------- phase 2 ----------------
for (int r0 = q0 + warp; r0 < q1; r0 += 8 * RB) {
uint4 nxt[RB][8];
const int rn = r0 + 8 * RB;
if (rn < q1) load_rows(nxt, rn);
#pragma unroll
for (int b = 0; b < RB; ++b) {
const int r = r0 + b * 8;
if (r < q1) {
const int slot = r / H, n = r - slot * H;
const float* hv = s_h + (slot == slot_a ? 0 : I);
float p = 0.f;
#pragma unroll
for (int t = 0; t < 8; ++t) {
const float* hp = hv + (t * 32 + lane) * 8;
const uint4 wv = cur[b][t];
const float4 h0 = *reinterpret_cast<const float4*>(hp);
const float4 h1 = *reinterpret_cast<const float4*>(hp + 4);
p = fmaf(bf_lo(wv.x), h0.x, p);
p = fmaf(bf_hi(wv.x), h0.y, p);
p = fmaf(bf_lo(wv.y), h0.z, p);
p = fmaf(bf_hi(wv.y), h0.w, p);
p = fmaf(bf_lo(wv.z), h1.x, p);
p = fmaf(bf_hi(wv.z), h1.y, p);
p = fmaf(bf_lo(wv.w), h1.z, p);
p = fmaf(bf_hi(wv.w), h1.w, p);
}
#pragma unroll
for (int o = 16; o > 0; o >>= 1) p += __shfl_xor_sync(0xffffffffu, p, o);
if (lane == 0) {
const float wj = (slot < K) ? __bfloat162float(wts[slot]) : 1.0f;
atomicAdd(acc + n, wj * p);
}
}
}
#pragma unroll
for (int b = 0; b < RB; ++b)
#pragma unroll
for (int t = 0; t < 8; ++t) cur[b][t] = nxt[b][t];
}
// ---------------- completion: last CTA converts and resets the scratch state ----------------
__threadfence();
__syncthreads();
if (tid == 0) {
unsigned prev = atomicAdd(done, 1u);
s_last = (prev == (unsigned)nb - 1);
}
__syncthreads();
if (s_last) {
__threadfence();
for (int n = tid; n < H; n += 256) {
out[n] = __float2bfloat16(__ldcg(acc + n));
acc[n] = 0.f;
}
if (tid == 0) { *bar = 0u; *done = 0u; }
}
}
torch::Tensor decode2_forward(torch::Tensor x, torch::Tensor ids, torch::Tensor wts, torch::Tensor w1r,
torch::Tensor w2r, torch::Tensor w1s, torch::Tensor w2s, torch::Tensor acc,
torch::Tensor counters, torch::Tensor hbuf) {
const int H = (int)x.size(1), K = (int)ids.size(1), I = (int)(w1r.size(1) / 2), NS = (int)w1s.size(0);
TORCH_CHECK(x.size(0) == 1 && H % 2048 == 0 && I == 2048, "decode2 path shape");
c10::cuda::CUDAGuard guard(x.device());
cudaStream_t stream = at::cuda::getCurrentCUDAStream();
auto out = torch::empty({1, H}, x.options());
const size_t smem = (size_t)H * 2 + 2 * (size_t)I * 4;
static int nsm = 0;
static bool init = false;
if (!init) {
cudaFuncSetAttribute(decode2_kernel, cudaFuncAttributeMaxDynamicSharedMemorySize, (int)smem);
cudaDeviceGetAttribute(&nsm, cudaDevAttrMultiProcessorCount, x.get_device());
init = true;
}
TORCH_CHECK(nsm >= K + NS, "decode2: a CTA's row range must span at most two expert slots");
const bf16* xp = reinterpret_cast<const bf16*>(x.data_ptr());
const int64_t* idp = ids.data_ptr<int64_t>();
const bf16* wp = reinterpret_cast<const bf16*>(wts.data_ptr());
const bf16* w1rp = reinterpret_cast<const bf16*>(w1r.data_ptr());
const bf16* w2rp = reinterpret_cast<const bf16*>(w2r.data_ptr());
const bf16* w1sp = reinterpret_cast<const bf16*>(w1s.data_ptr());
const bf16* w2sp = reinterpret_cast<const bf16*>(w2s.data_ptr());
float* hp = hbuf.data_ptr<float>();
float* accp = acc.data_ptr<float>();
unsigned* bar = reinterpret_cast<unsigned*>(counters.data_ptr<int>());
unsigned* done = bar + 1;
bf16* outp = reinterpret_cast<bf16*>(out.data_ptr());
int NSLOT = K + NS;
void* args[] = {(void*)&xp, (void*)&idp, (void*)&wp, (void*)&w1rp, (void*)&w2rp, (void*)&w1sp, (void*)&w2sp,
(void*)&hp, (void*)&accp, (void*)&bar, (void*)&done, (void*)&outp, (void*)&K, (void*)&NSLOT,
(void*)&H, (void*)&I};
cudaError_t e = cudaLaunchCooperativeKernel((const void*)decode2_kernel, dim3(nsm), dim3(256), args, smem, stream);
TORCH_CHECK(e == cudaSuccess, "decode2 cooperative launch failed: ", cudaGetErrorString(e));
return out;
}
// Entry point: the decode kernel for a single token (when the shapes allow it), the grouped-GEMM pipeline otherwise.
torch::Tensor moe_forward_entry(torch::Tensor x, torch::Tensor ids, torch::Tensor wts, torch::Tensor w1r,
torch::Tensor w2r, torch::Tensor w1s, torch::Tensor w2s) {
TORCH_CHECK(x.is_cuda() && x.dim() == 2 && x.scalar_type() == torch::kBFloat16 && x.is_contiguous(),
"x must be a contiguous bf16 cuda matrix");
TORCH_CHECK(ids.is_cuda() && ids.dim() == 2 && ids.size(0) == x.size(0) && ids.scalar_type() == torch::kInt64 &&
ids.is_contiguous(),
"expert_ids must be a contiguous int64 [T, top_k] cuda tensor");
TORCH_CHECK(wts.is_cuda() && wts.sizes() == ids.sizes() && wts.scalar_type() == torch::kBFloat16 && wts.is_contiguous(),
"expert_weights must be a contiguous bf16 [T, top_k] cuda tensor");
for (const torch::Tensor* w : {&w1r, &w2r, &w1s, &w2s})
TORCH_CHECK(w->is_cuda() && w->dim() == 3 && w->scalar_type() == torch::kBFloat16 && w->is_contiguous(),
"expert weights must be contiguous bf16 3-D cuda tensors");
const int64_t H = x.size(1), I = w1r.size(1) / 2;
TORCH_CHECK(w1r.size(1) == 2 * I && w1r.size(2) == H && w2r.size(0) == w1r.size(0) && w2r.size(1) == H &&
w2r.size(2) == I && w1s.size(1) == 2 * I && w1s.size(2) == H && w2s.size(0) == w1s.size(0) &&
w2s.size(1) == H && w2s.size(2) == I,
"inconsistent weight shapes");
if (x.size(0) == 1 && I == 2048 && H % 2048 == 0 && ids.size(1) + w1s.size(0) <= 16) {
static torch::Tensor accs[16], cnts[16], hbufs[16];
const int dev = (int)x.get_device();
const int64_t nslot = ids.size(1) + w1s.size(0);
if (!accs[dev].defined() || accs[dev].numel() < H || hbufs[dev].numel() < nslot * I) {
accs[dev] = torch::zeros({H}, x.options().dtype(torch::kFloat32));
cnts[dev] = torch::zeros({2}, x.options().dtype(torch::kInt32));
hbufs[dev] = torch::zeros({nslot * I}, x.options().dtype(torch::kFloat32));
}
return decode2_forward(x, ids, wts, w1r, w2r, w1s, w2s, accs[dev], cnts[dev], hbufs[dev]);
}
return moe_forward(x, ids, wts, w1r, w2r, w1s, w2s);
}
PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
m.def("forward", &moe_forward_entry, "GLM5.2 fused MoE forward");
}
'''
_ext = load_inline(
name="glm52_moe_sm120_v6",
cpp_sources="",
cuda_sources=_CUDA_SRC,
functions=None,
extra_cuda_cflags=["-O3", "-lineinfo"],
verbose=False,
)
_forward = _ext.forward
T, E, top_k, n_shared, H, I = 4096, 256, 8, 1, 4096, 2048
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
# Allocate straight on the GPU: the fused expert tables are ~13 GB and must not transit host memory.
dev = "cuda" if torch.cuda.is_available() else "cpu"
self.w1_routed = nn.Parameter(torch.empty(E, 2 * I, H, dtype=torch.bfloat16, device=dev))
self.w2_routed = nn.Parameter(torch.empty(E, H, I, dtype=torch.bfloat16, device=dev))
self.w1_shared = nn.Parameter(torch.empty(n_shared, 2 * I, H, dtype=torch.bfloat16, device=dev))
self.w2_shared = nn.Parameter(torch.empty(n_shared, H, I, dtype=torch.bfloat16, device=dev))
for p in self.parameters():
nn.init.normal_(p, std=0.02)
def forward(self, x: torch.Tensor, expert_ids: torch.Tensor, expert_weights: torch.Tensor) -> torch.Tensor:
"""x (T,H) bf16, expert_ids (T,top_k) int64, expert_weights (T,top_k) bf16 -> (T,H) bf16."""
return _forward(x, expert_ids, expert_weights, self.w1_routed, self.w2_routed, self.w1_shared, self.w2_shared)
def get_init_inputs():
return [T, E, top_k, n_shared, H, I]
def get_inputs():
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]
20260928_122238_claude_claude-sonnet-5-5_01_glm52_fused_moe