KernelBench hard · RTX PRO 6000
W4A16 GEMM Kimi K3 (256k)
manually audited: clean
Genuine fused W4A16 (int4-weight, bf16-activation) GEMM with three real paths: an M==1 GEMV using the fp32-magic lop3 dequant trick (split-K, atomic combine with self-resetting counters), an M>=2 TMA-staged mma.sync.m16n8k16 kernel over repacked int4 weights with a split-K fp32 epilogue, and a Triton fused-dequant fallback for odd shapes. Dequant applies scales and zeros per 128-group from live buffers on every call; both custom kernels read the live activation tensor and write to a freshly allocated output (at::empty per call). The only cache is the repacked weight layout (_wr), keyed on weights and invalidated on load_state_dict and device change — the legitimate W4A16 pattern, not output caching. 0.0425 geomean peak fraction is a real timing (decode shape 0.079, M=256 prefill 0.014).
Kernel source (redacted)
"""W4A16 weight-only quantized GEMM for B200 (SM100).
Fused int4-unpack + GEMM. Custom paths:
- M == 1: fp32-magic int4 GEMV (split-K, one kernel).
- M >= 2: TMA-staged mma.sync kernel with repacked weights, split-K combine.
- Fallback: Triton fused-dequant kernel for odd shapes.
"""
from __future__ import annotations
import torch
import torch.nn as nn
OP_TYPE = "gemm_w4a16"
SUPPORTED_PRECISIONS = ["int4_bf16"]
HARDWARE_REQUIRED = ["B200"]
GROUP_SIZE = 128
# ----------------------------------------------------------------------------
# Extension sources (GEMV + MMA built once at import)
# ----------------------------------------------------------------------------
_CPP_DECL = r"""
#include <torch/extension.h>
torch::Tensor repack(torch::Tensor wq);
torch::Tensor gemv_w4(torch::Tensor x, torch::Tensor wq, torch::Tensor sc, torch::Tensor zr,
torch::Tensor wksp, torch::Tensor counters, long ksplit, long gs, long wpg, long unroll);
torch::Tensor w4_mma(torch::Tensor x, torch::Tensor wr, torch::Tensor sc, torch::Tensor zr,
torch::Tensor wksp, torch::Tensor counters, long variant, long splitk, long dbg);
"""
_GEMV_SRC = r'''
#include <torch/extension.h>
#include <ATen/cuda/CUDAContext.h>
#include <cuda_runtime.h>
#include <cuda_bf16.h>
#define MAGICV 0x46000000u
template <unsigned IMM>
__device__ __forceinline__ unsigned lop3v(unsigned a, unsigned b, unsigned c) {
unsigned r;
asm("lop3.b32 %0, %1, %2, %3, %4;" : "=r"(r) : "r"(a), "r"(b), "r"(c), "n"(IMM));
return r;
}
__device__ __forceinline__ uint4 ld_ef(const void* p, unsigned long long pol) {
uint4 v;
asm volatile("ld.global.nc.L2::cache_hint.v4.u32 {%0,%1,%2,%3}, [%4], %5;"
: "=r"(v.x), "=r"(v.y), "=r"(v.z), "=r"(v.w) : "l"(p), "l"(pol));
return v;
}
struct alignas(32) b32x { unsigned long long a, b, c, d; };
__device__ __forceinline__ b32x ld_sz(const void* p) {
b32x v;
asm volatile("ld.global.nc.v4.u64 {%0,%1,%2,%3}, [%4];"
: "=l"(v.a), "=l"(v.b), "=l"(v.c), "=l"(v.d) : "l"(p));
return v;
}
__device__ __forceinline__ void unpack_bf16_16(b32x v, float* f) {
unsigned long long vs[4] = {v.a, v.b, v.c, v.d};
#pragma unroll
for (int q = 0; q < 4; q++) {
unsigned lo = (unsigned)(vs[q] & 0xFFFFFFFFull);
unsigned hi = (unsigned)(vs[q] >> 32);
f[4*q+0] = __uint_as_float(lo << 16);
f[4*q+1] = __uint_as_float(lo & 0xFFFF0000u);
f[4*q+2] = __uint_as_float(hi << 16);
f[4*q+3] = __uint_as_float(hi & 0xFFFF0000u);
}
}
__device__ __constant__ float PAE_TAB[4] = {0x1p10f, 0x1p2f, 0x1p-6f, 0x1p-6f};
template <int GS, int WPG, int UNROLL>
__global__ void __launch_bounds__(32 * GS * WPG) gemv_w4_kernel(
const unsigned char* __restrict__ wq,
const __nv_bfloat16* __restrict__ x,
const __nv_bfloat16* __restrict__ sc,
const __nv_bfloat16* __restrict__ zr,
__nv_bfloat16* __restrict__ out,
float* __restrict__ wksp,
int* __restrict__ counters,
int N, int KH, int ksplit)
{
constexpr int WY = GS * WPG;
const int lane = threadIdx.x;
const int wy = threadIdx.y;
const int tile = blockIdx.y;
const int slice = blockIdx.x;
const int col0 = tile * 512 + lane * 16;
const int g_local = wy / WPG;
const int r_off = wy % WPG;
const int g0 = slice * GS + g_local;
const int row0 = g0 * 64 + r_off;
float acc[16];
#pragma unroll
for (int i = 0; i < 16; i++) acc[i] = 0.f;
unsigned long long pol;
asm("createpolicy.fractional.L2::evict_first.b64 %0, 1.0;" : "=l"(pol));
float A[16], C[16], Dz[16];
{
b32x sv = ld_sz(sc + (size_t)g0 * N + col0);
b32x zv = ld_sz(zr + (size_t)g0 * N + col0);
float s[16], z[16];
unpack_bf16_16(sv, s);
unpack_bf16_16(zv, z);
#pragma unroll
for (int i = 0; i < 16; i++) {
int b = i & 3;
A[i] = s[i] * PAE_TAB[b];
C[i] = -8192.f * A[i];
Dz[i] = -s[i] * z[i];
}
}
float gsum = 0.f;
const __nv_bfloat16* xp = x + (size_t)row0 * 2;
const unsigned char* wp = wq + (size_t)row0 * N + col0;
constexpr int ROWS = 64 / WPG;
#pragma unroll
for (int rb = 0; rb < ROWS; rb += UNROLL) {
uint4 v[UNROLL];
float xe[UNROLL], xo[UNROLL], xo16[UNROLL];
#pragma unroll
for (int u = 0; u < UNROLL; u++) {
v[u] = ld_ef(wp + (size_t)(rb + u) * WPG * N, pol);
unsigned xv = *(const unsigned*)(xp + (size_t)(rb + u) * WPG * 2);
xo[u] = __uint_as_float(xv & 0xFFFF0000u);
xe[u] = __uint_as_float(xv << 16);
xo16[u] = xo[u] * 0.0625f;
gsum = gsum + xe[u] + xo[u];
}
#pragma unroll
for (int u = 0; u < UNROLL; u++) {
unsigned ws[4] = {v[u].x, v[u].y, v[u].z, v[u].w};
#pragma unroll
for (int j = 0; j < 4; j++) {
unsigned wu = ws[j];
{
unsigned lo = lop3v<0xEA>(wu, 0x0000000Fu, MAGICV);
float te = fmaf(A[4*j+0], __uint_as_float(lo), C[4*j+0]);
acc[4*j+0] = fmaf(xe[u], te, acc[4*j+0]);
unsigned hi = lop3v<0xEA>(wu, 0x000000F0u, MAGICV);
float to = fmaf(A[4*j+0], __uint_as_float(hi), C[4*j+0]);
acc[4*j+0] = fmaf(xo16[u], to, acc[4*j+0]);
}
{
unsigned lo = lop3v<0xEA>(wu, 0x00000F00u, MAGICV);
float te = fmaf(A[4*j+1], __uint_as_float(lo), C[4*j+1]);
acc[4*j+1] = fmaf(xe[u], te, acc[4*j+1]);
unsigned hi = lop3v<0xEA>(wu, 0x0000F000u, MAGICV);
float to = fmaf(A[4*j+1], __uint_as_float(hi), C[4*j+1]);
acc[4*j+1] = fmaf(xo16[u], to, acc[4*j+1]);
}
{
unsigned lo = lop3v<0xEA>(wu, 0x000F0000u, MAGICV);
float te = fmaf(A[4*j+2], __uint_as_float(lo), C[4*j+2]);
acc[4*j+2] = fmaf(xe[u], te, acc[4*j+2]);
unsigned hi = lop3v<0xEA>(wu >> 4, 0x000F0000u, MAGICV);
float to = fmaf(A[4*j+2], __uint_as_float(hi), C[4*j+2]);
acc[4*j+2] = fmaf(xo[u], to, acc[4*j+2]);
}
{
unsigned lo = lop3v<0xEA>(wu >> 8, 0x000F0000u, MAGICV);
float te = fmaf(A[4*j+3], __uint_as_float(lo), C[4*j+3]);
acc[4*j+3] = fmaf(xe[u], te, acc[4*j+3]);
unsigned hi = lop3v<0xEA>(wu >> 12, 0x000F0000u, MAGICV);
float to = fmaf(A[4*j+3], __uint_as_float(hi), C[4*j+3]);
acc[4*j+3] = fmaf(xo[u], to, acc[4*j+3]);
}
}
}
}
#pragma unroll
for (int i = 0; i < 16; i++) acc[i] = fmaf(Dz[i], gsum, acc[i]);
__shared__ float reduce[WY][512];
__shared__ int done_flag;
if (WY > 1) {
#pragma unroll
for (int i = 0; i < 16; i++) reduce[wy][i * 32 + lane] = acc[i];
__syncthreads();
if (wy == 0) {
#pragma unroll
for (int i = 0; i < 16; i++) {
float t = 0.f;
#pragma unroll
for (int yy = 0; yy < WY; yy++) t += reduce[yy][i * 32 + lane];
acc[i] = t;
}
}
__syncthreads();
}
if (wy == 0) {
float* wp_out = wksp + tile * 512 + lane * 16;
#pragma unroll
for (int i = 0; i < 16; i++) atomicAdd(wp_out + i, acc[i]);
}
__threadfence();
__syncthreads();
if (threadIdx.x == 0 && threadIdx.y == 0) {
int old = atomicAdd(&counters[tile], 1);
done_flag = (old == ksplit - 1) ? 1 : 0;
}
__threadfence();
__syncthreads();
if (done_flag && wy == 0) {
float su[16];
const float* rp = wksp + tile * 512 + lane * 16;
#pragma unroll
for (int i = 0; i < 16; i++) su[i] = rp[i];
__nv_bfloat16* op = out + tile * 512 + lane * 16;
#pragma unroll
for (int i = 0; i < 16; i++) op[i] = __float2bfloat16(su[i]);
float* zp = wksp + tile * 512 + lane * 16;
#pragma unroll
for (int i = 0; i < 16; i++) zp[i] = 0.f;
if (lane == 0) counters[tile] = 0;
}
}
#define DISPATCH_GEMV(GS_, WPG_, UNROLL_) \
gemv_w4_kernel<GS_, WPG_, UNROLL_><<<grid, dim3(32, (GS_) * (WPG_)), 0, stream>>>( \
(const unsigned char*)wq.data_ptr(), (const __nv_bfloat16*)x.data_ptr(), \
(const __nv_bfloat16*)sc.data_ptr(), (const __nv_bfloat16*)zr.data_ptr(), \
(__nv_bfloat16*)out.data_ptr(), wksp.data_ptr<float>(), \
counters.data_ptr<int>(), N, KH, (int)ksplit)
torch::Tensor gemv_w4(torch::Tensor x, torch::Tensor wq, torch::Tensor sc, torch::Tensor zr,
torch::Tensor wksp, torch::Tensor counters, long ksplit, long gs, long wpg, long unroll) {
int N = wq.size(1);
int KH = wq.size(0);
auto out = at::empty({N}, x.options());
dim3 grid(ksplit, N / 512);
auto stream = at::cuda::getCurrentCUDAStream();
TORCH_CHECK(32 / ksplit == gs, "ksplit*gs must be 32");
if (gs == 2 && wpg == 2 && unroll == 8) { DISPATCH_GEMV(2, 2, 8); }
else if (gs == 1 && wpg == 4 && unroll == 8) { DISPATCH_GEMV(1, 4, 8); }
else if (gs == 1 && wpg == 4 && unroll == 4) { DISPATCH_GEMV(1, 4, 4); }
else if (gs == 1 && wpg == 2 && unroll == 8) { DISPATCH_GEMV(1, 2, 8); }
else if (gs == 2 && wpg == 4 && unroll == 8) { DISPATCH_GEMV(2, 4, 8); }
else if (gs == 4 && wpg == 2 && unroll == 8) { DISPATCH_GEMV(4, 2, 8); }
else if (gs == 4 && wpg == 4 && unroll == 8) { DISPATCH_GEMV(4, 4, 8); }
else if (gs == 2 && wpg == 2 && unroll == 4) { DISPATCH_GEMV(2, 2, 4); }
else if (gs == 1 && wpg == 1 && unroll == 8) { DISPATCH_GEMV(1, 1, 8); }
else { TORCH_CHECK(false, "no such variant"); }
return out;
}
'''
_MMA_SRC = r'''
//
// W4A16 fused mma kernel (bf16, SM100). v6
//
// Same repack/dequant/fragment scheme as v5 (see w4mma_kernel history).
// v6 changes:
// - two-chunk software pipeline: LDS + dequant for chunks (2c, 2c+1) issued
// before the mma cluster of chunk 2c to overlap HMMA issue with unpack.
// - split-K epilogue: fp32 partials with plain vector stores (no atomic RMW
// storm), one fence, per-tile semaphore, last block reads SPLITK partial
// slabs, sums, converts to bf16, re-zeroes them for the next call.
// - globaltimer STAMP hooks for profiling.
#include <torch/extension.h>
#include <ATen/cuda/CUDAContext.h>
#include <cuda_runtime.h>
#include <cuda_bf16.h>
#include <cstdint>
// ---------------------------------------------------------------------------
// repack kernel: one thread per (n32-tile, k-chunk, lane) -> uint2
// ---------------------------------------------------------------------------
__global__ void repack_w4(const unsigned char* __restrict__ wq,
uint2* __restrict__ wr,
int N, int KH) {
int n32tile = blockIdx.x;
int chunk = blockIdx.y; // 256
int lane = threadIdx.x;
int n128 = n32tile / 4;
int st = n32tile % 4;
int col_base = n128 * 128 + st * 32;
int s = chunk / 8, ch = chunk % 8;
int br0 = chunk * 8;
unsigned q[2] = {0u, 0u};
#pragma unroll
for (int j = 0; j < 2; j++) {
#pragma unroll
for (int half = 0; half < 2; half++) {
int t = 2 * j + half;
int col = col_base + 8 * t + lane / 4;
int rl = br0 + (lane % 4);
int rh = br0 + (lane % 4) + 4;
unsigned bl = wq[(size_t)rl * N + col];
unsigned bh = wq[(size_t)rh * N + col];
if (half == 0) {
q[j] |= (bl & 0xF);
q[j] |= (bl & 0xF0);
q[j] |= (bh & 0xF) << 8;
q[j] |= (bh & 0xF0) << 12;
} else {
q[j] |= (bl & 0xF) << 12;
q[j] |= (bl & 0xF0) << 16;
q[j] |= (bh & 0xF) << 24;
q[j] |= (bh & 0xF0) << 24;
}
}
}
size_t u2 = ((((size_t)n128 * 32 + s) * 4 + st) * 8 + ch) * 32 + lane;
wr[u2] = make_uint2(q[0], q[1]);
}
torch::Tensor repack(torch::Tensor wq) {
int KH = wq.size(0);
int N = wq.size(1);
TORCH_CHECK(N % 128 == 0 && KH == 2048, "shape");
auto wr = at::zeros({(long)N * KH / 8 * 2}, wq.options().dtype(at::kInt));
repack_w4<<<dim3((N / 128) * 4, 256), 32, 0, at::cuda::getCurrentCUDAStream()>>>(
(const unsigned char*)wq.data_ptr(), (uint2*)wr.data_ptr(), N, KH);
return wr;
}
// ---------------------------------------------------------------------------
// helpers
// ---------------------------------------------------------------------------
template <unsigned IMM>
__device__ __forceinline__ unsigned lop3(unsigned a, unsigned b, unsigned c) {
unsigned r;
asm("lop3.b32 %0, %1, %2, %3, %4;" : "=r"(r) : "r"(a), "r"(b), "r"(c), "n"(IMM));
return r;
}
__device__ __forceinline__ void mma_bf16(float c[4], const unsigned a[4], const unsigned b[2]) {
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"(b[0]), "r"(b[1]));
}
__device__ __forceinline__ unsigned pack_bf16x2(float a_hi, float b_lo) {
unsigned r;
asm("cvt.rn.bf16x2.f32 %0, %1, %2;" : "=r"(r) : "f"(a_hi), "f"(b_lo));
return r;
}
__device__ __forceinline__ void mbar_init(uint64_t* bar, uint32_t count) {
uint32_t s = static_cast<uint32_t>(__cvta_generic_to_shared(bar));
asm volatile("mbarrier.init.shared.b64 [%0], %1;" :: "r"(s), "r"(count));
}
__device__ __forceinline__ void mbar_expect(uint64_t* bar, uint32_t bytes) {
uint32_t b = static_cast<uint32_t>(__cvta_generic_to_shared(bar));
asm volatile(
"{\n\t.reg .b64 t1;\n\tmbarrier.arrive.expect_tx.release.cta.shared::cta.b64 t1, [%0], %1;\n\t}"
:: "r"(b), "r"(bytes));
}
__device__ __forceinline__ void tma_bulk(void* smem, const void* glob, uint32_t bytes, uint64_t* bar, unsigned long long pol) {
uint32_t s = static_cast<uint32_t>(__cvta_generic_to_shared(smem));
uint32_t b = static_cast<uint32_t>(__cvta_generic_to_shared(bar));
asm volatile(
"cp.async.bulk.shared::cta.global.mbarrier::complete_tx::bytes.L2::cache_hint [%0], [%1], %2, [%3], %4;"
:: "r"(s), "l"(glob), "r"(bytes), "r"(b), "l"(pol));
}
__device__ __forceinline__ void mbar_wait(uint64_t* bar, uint32_t parity) {
uint32_t b = static_cast<uint32_t>(__cvta_generic_to_shared(bar));
asm volatile(
"{\n\t.reg .pred p;\n\t"
"WAIT_%=:\n\t"
"mbarrier.try_wait.parity.acquire.cta.shared::cta.b64 p, [%0], %1;\n\t"
"@!p bra WAIT_%=;\n\t}"
:: "r"(b), "r"(parity));
}
#define MAGIC 0x46000000u
__device__ __constant__ float PAE_TA[4] = {0x1p10f, 0x1p6f, 0x1p2f, 0x1p-6f}; // tile A {0,4,8,16}
__device__ __constant__ float PAE_TB[4] = {0x1p10f, 0x1p2f, 0x1p-2f, 0x1p-6f}; // tile B {0,8,12,16}
template <int TILE>
__device__ __forceinline__ void dequant_tile(unsigned q, unsigned out[2],
const float A[4], const float C[4], float tz) {
unsigned v0, v1, v2, v3;
if (TILE == 0) {
v0 = lop3<0xEA>(q, 0x0000000Fu, MAGIC);
v1 = lop3<0xEA>(q, 0x000000F0u, MAGIC);
v2 = lop3<0xEA>(q, 0x00000F00u, MAGIC);
v3 = lop3<0xEA>(q, 0x000F0000u, MAGIC);
} else {
unsigned qq = q >> 12;
v0 = lop3<0xEA>(qq, 0x0000000Fu, MAGIC);
v1 = lop3<0xEA>(qq, 0x00000F00u, MAGIC);
v2 = lop3<0xEA>(qq, 0x0000F000u, MAGIC);
v3 = lop3<0xEA>(qq, 0x000F0000u, MAGIC);
}
float t0 = fmaf(A[0], __uint_as_float(v0), C[0]) + tz;
float t1 = fmaf(A[1], __uint_as_float(v1), C[1]) + tz;
float t2 = fmaf(A[2], __uint_as_float(v2), C[2]) + tz;
float t3 = fmaf(A[3], __uint_as_float(v3), C[3]) + tz;
out[0] = pack_bf16x2(t1, t0);
out[1] = pack_bf16x2(t3, t2);
}
// ---------------------------------------------------------------------------
// main kernel
// ---------------------------------------------------------------------------
template <int MT, int SPLITK, int STAGES, int DBGM=0>
__global__ void __launch_bounds__(128) w4_mma_kernel(
const uint2* __restrict__ wr,
const __nv_bfloat16* __restrict__ x,
const __nv_bfloat16* __restrict__ sc,
const __nv_bfloat16* __restrict__ zr,
__nv_bfloat16* __restrict__ out,
float* __restrict__ wksp,
int* __restrict__ counters,
int M, int N, int K,
unsigned long long* __restrict__ tbuf)
{
constexpr int CHUNKS = 256 / SPLITK;
constexpr int N_STAGES_TOTAL = CHUNKS / 8;
constexpr int SZ_OFF = STAGES * 8192;
const int lane = threadIdx.x & 31;
const int warp = threadIdx.x >> 5;
const int n128 = blockIdx.y;
const int slice = blockIdx.x;
const int mb = blockIdx.z;
const int chunk_base = slice * CHUNKS;
const int row_base = mb * 16 * MT;
const int tid = threadIdx.x;
extern __shared__ __align__(16) unsigned char smem[];
__shared__ __align__(8) uint64_t bars[STAGES];
__shared__ int done_flag;
auto stamp = [&]() { unsigned long long t; asm volatile("mov.u64 %0, %globaltimer;" : "=l"(t)); return t; };
#define STAMP(phase) if (tbuf && tid == 0) tbuf[((size_t)(mb * gridDim.y + n128) * gridDim.x + slice) * 8 + (phase)] = stamp()
float c[MT][4][4];
#pragma unroll
for (int i = 0; i < MT; i++)
#pragma unroll
for (int j = 0; j < 4; j++)
#pragma unroll
for (int u = 0; u < 4; u++) c[i][j][u] = 0.f;
unsigned long long pol;
asm("createpolicy.fractional.L2::evict_first.b64 %0, 1.0;" : "=l"(pol));
if (tid == 0) {
#pragma unroll
for (int s = 0; s < STAGES; s++) mbar_init(&bars[s], 1);
}
__syncthreads();
auto fetch = [&](int s) {
if (s >= N_STAGES_TOTAL) return;
int slot = s % STAGES;
int group = chunk_base / 8 + s;
if (tid == 0) {
mbar_expect(&bars[slot], 8192 + 512);
size_t u2_global = (((size_t)n128 * 32 + group) * 4 * 8 * 32);
tma_bulk(&smem[slot * 8192], (const unsigned char*)wr + u2_global * 8, 8192, &bars[slot], pol);
tma_bulk(&smem[SZ_OFF + slot * 512], sc + (size_t)group * N + n128 * 128, 256, &bars[slot], pol);
tma_bulk(&smem[SZ_OFF + slot * 512 + 256], zr + (size_t)group * N + n128 * 128, 256, &bars[slot], pol);
}
};
for (int s = 0; s < STAGES - 1; s++) fetch(s);
float A[4][4], C[4][4], tz[4];
// x slice pointers (via L1, prefetched at start of each group-epoch)
STAMP(0);
for (int s = 0; s < N_STAGES_TOTAL; s++) {
int slot = s % STAGES;
mbar_wait(&bars[slot], (s / STAGES) & 1);
if (s == 0) STAMP(1);
{
const __nv_bfloat16* shS = (const __nv_bfloat16*)&smem[SZ_OFF + slot * 512];
const __nv_bfloat16* shZ = (const __nv_bfloat16*)&smem[SZ_OFF + slot * 512 + 256];
#pragma unroll
for (int t = 0; t < 4; t++) {
int col = warp * 32 + 8 * t + lane / 4;
float sf = __bfloat162float(shS[col]);
float zf = __bfloat162float(shZ[col]);
const float* pae = (t % 2 == 0) ? PAE_TA : PAE_TB;
#pragma unroll
for (int u = 0; u < 4; u++) {
A[t][u] = sf * pae[u];
C[t][u] = -8192.f * A[t][u];
}
tz[t] = -sf * zf;
}
}
// A-frag x loads via L1 (global); prefetch next group while computing
__nv_bfloat16* __restrict__ xg = (__nv_bfloat16*)x;
// two-chunk software pipeline: (load+dequant of ch pair) then mma cluster
#pragma unroll
for (int ch2 = 0; ch2 < 4; ch2++) {
if (s == 0) STAMP(2 + ch2);
unsigned a[2][MT][4];
#pragma unroll
for (int cc = 0; cc < 2; cc++) {
int ch = ch2 * 2 + cc;
long kk = ((long)chunk_base + s * 8 + ch) * 16 + 2 * (lane % 4);
#pragma unroll
for (int i = 0; i < MT; i++) {
if (DBGM == 2 || DBGM == 3) { a[cc][i][0]=0u; a[cc][i][1]=1u; a[cc][i][2]=2u; a[cc][i][3]=3u; } else {
int r0g = row_base + i * 16 + lane / 4;
int r1g = r0g + 8;
bool p0 = r0g < M, p1 = r1g < M;
const __nv_bfloat16* x0 = xg + (size_t)r0g * K + kk;
const __nv_bfloat16* x1 = xg + (size_t)r1g * K + kk;
a[cc][i][0] = p0 ? __ldg((const unsigned*)x0) : 0u;
a[cc][i][1] = p1 ? __ldg((const unsigned*)x1) : 0u;
a[cc][i][2] = p0 ? __ldg((const unsigned*)(x0 + 8)) : 0u;
a[cc][i][3] = p1 ? __ldg((const unsigned*)(x1 + 8)) : 0u;
}
}
}
uint2 qA = ((uint2*)&smem[slot * 8192])[(warp * 8 + ch2 * 2) * 32 + lane];
uint2 qB = ((uint2*)&smem[slot * 8192])[(warp * 8 + ch2 * 2 + 1) * 32 + lane];
unsigned bA[4][4], bB[4][4];
#pragma unroll
for (int j = 0; j < 2; j++) {
unsigned qa = j == 0 ? qA.x : qA.y;
unsigned qb = j == 0 ? qB.x : qB.y;
if (DBGM == 1 || DBGM == 3) {
bA[2*j+0][0] = qa; bA[2*j+0][1] = qa >> 8; bA[2*j+1][0] = qa; bA[2*j+1][1] = qa >> 8;
bB[2*j+0][0] = qb; bB[2*j+0][1] = qb >> 8; bB[2*j+1][0] = qb; bB[2*j+1][1] = qb >> 8;
} else {
dequant_tile<0>(qa, (unsigned*)&bA[2*j+0], A[2*j+0], C[2*j+0], tz[2*j+0]);
dequant_tile<1>(qa, (unsigned*)&bA[2*j+1], A[2*j+1], C[2*j+1], tz[2*j+1]);
dequant_tile<0>(qb, (unsigned*)&bB[2*j+0], A[2*j+0], C[2*j+0], tz[2*j+0]);
dequant_tile<1>(qb, (unsigned*)&bB[2*j+1], A[2*j+1], C[2*j+1], tz[2*j+1]);
}
}
#pragma unroll
for (int t = 0; t < 4; t++) {
#pragma unroll
for (int i = 0; i < MT; i++) {
mma_bf16(c[i][t], a[0][i], (unsigned*)&bA[t]);
}
}
#pragma unroll
for (int t = 0; t < 4; t++) {
#pragma unroll
for (int i = 0; i < MT; i++) {
mma_bf16(c[i][t], a[1][i], (unsigned*)&bB[t]);
}
}
}
if (s == 0) STAMP(3);
__syncthreads();
fetch(s + STAGES - 1);
}
STAMP(4);
if (SPLITK == 1) {
#pragma unroll
for (int i = 0; i < MT; i++) {
#pragma unroll
for (int t = 0; t < 4; t++) {
int col0 = n128 * 128 + warp * 32 + 8 * t + 2 * (lane % 4);
int r0 = row_base + i * 16 + lane / 4;
unsigned v01 = pack_bf16x2(c[i][t][1], c[i][t][0]);
unsigned v23 = pack_bf16x2(c[i][t][3], c[i][t][2]);
if (r0 < M) *(unsigned*)(out + (size_t)r0 * N + col0) = v01;
if (r0 + 8 < M) *(unsigned*)(out + (size_t)(r0 + 8) * N + col0) = v23;
}
}
} else {
// fp32 partials to per-slice slabs [slice][row][col], plain vector stores
#pragma unroll
for (int i = 0; i < MT; i++) {
#pragma unroll
for (int t = 0; t < 4; t++) {
int col0 = n128 * 128 + warp * 32 + 8 * t + 2 * (lane % 4);
int r0 = row_base + i * 16 + lane / 4;
if (r0 < M) {
float2 v01 = make_float2(c[i][t][0], c[i][t][1]);
*(float2*)&wksp[(((size_t)slice * M + r0) * N) + col0] = v01;
}
if (r0 + 8 < M) {
float2 v23 = make_float2(c[i][t][2], c[i][t][3]);
*(float2*)&wksp[(((size_t)slice * M + r0 + 8) * N) + col0] = v23;
}
}
}
__threadfence();
__syncthreads();
if (tid == 0) {
int idx = mb * (N / 128) + n128;
int old = atomicAdd(&counters[idx], 1);
done_flag = (old == SPLITK - 1) ? 1 : 0;
}
__syncthreads();
if (done_flag) {
__threadfence();
int idx = mb * (N / 128) + n128;
if (tid == 0) counters[idx] = 0;
int rows = min(16 * MT, M - row_base);
for (int r = 0; r < rows; r++) {
int cr = row_base + r;
// 128 threads: each thread does one column (scalar; region = 128 cols)
int cc = n128 * 128 + tid;
float sum = 0.f;
#pragma unroll
for (int sl = 0; sl < SPLITK; sl++) sum += wksp[(((size_t)sl * M + cr) * N) + cc];
out[(size_t)cr * N + cc] = __float2bfloat16(sum);
#pragma unroll
for (int sl = 0; sl < SPLITK; sl++) wksp[(((size_t)sl * M + cr) * N) + cc] = 0.f;
}
}
}
STAMP(5);
#undef STAMP
}
// ---------------------------------------------------------------------------
// host dispatch
// ---------------------------------------------------------------------------
static int smem_bytes(int stages, int mt) {
return stages * (8192 + 512);
}
static unsigned long long* tbuf_ptr = nullptr;
void set_tbuf(uint64_t* p) { tbuf_ptr = (unsigned long long*)p; }
template <int MT, int SPLITK, int STAGES, int DBGM_>
static void launch_v4(dim3 grid, cudaStream_t stream, torch::Tensor& x, torch::Tensor& wr,
torch::Tensor& sc, torch::Tensor& zr, torch::Tensor& out,
torch::Tensor& wksp, torch::Tensor& counters, int M, int N, int K) {
int bytes = smem_bytes(STAGES, MT);
static int configured = 0;
if (bytes > 48 * 1024 && !configured) {
cudaError_t aerr = cudaFuncSetAttribute(w4_mma_kernel<MT, SPLITK, STAGES, DBGM_>,
cudaFuncAttributeMaxDynamicSharedMemorySize, bytes);
TORCH_CHECK(aerr == cudaSuccess, "attr set failed: ", cudaGetErrorString(aerr), " (bytes=", bytes, ")");
configured = 1;
}
w4_mma_kernel<MT, SPLITK, STAGES, DBGM_><<<grid, 128, bytes, stream>>>(
(const uint2*)wr.data_ptr(), (const __nv_bfloat16*)x.data_ptr(),
(const __nv_bfloat16*)sc.data_ptr(), (const __nv_bfloat16*)zr.data_ptr(),
(__nv_bfloat16*)out.data_ptr(), wksp.data_ptr<float>(), counters.data_ptr<int>(), M, N, K,
tbuf_ptr);
cudaError_t err = cudaGetLastError();
TORCH_CHECK(err == cudaSuccess, "kernel launch failed: ", cudaGetErrorString(err), " (bytes=", bytes, ")");
}
torch::Tensor w4_mma_set_tbuf(torch::Tensor t) { set_tbuf((uint64_t*)t.data_ptr()); return t; }
torch::Tensor w4_mma(torch::Tensor x, torch::Tensor wr, torch::Tensor sc, torch::Tensor zr,
torch::Tensor wksp, torch::Tensor counters,
long variant, long splitk, long dbg) {
int M = x.size(0);
int K = x.size(1);
int N = sc.size(1);
auto out = at::empty({M, N}, x.options());
auto stream = at::cuda::getCurrentCUDAStream();
#define LAUNCH(MT_, SPLITK_, STAGES_) \
{ dim3 grid(SPLITK_, N / 128, (M + 16 * MT_ - 1) / (16 * MT_)); \
launch_v4<MT_, SPLITK_, STAGES_, 0>(grid, stream, x, wr, sc, zr, out, wksp, counters, M, N, K); }
if (dbg == 1) { launch_v4<1, 4, 4, 1>(dim3(4, N/128, 1), stream, x, wr, sc, zr, out, wksp, counters, M, N, K); return out; }
if (dbg == 2) { launch_v4<1, 4, 4, 2>(dim3(4, N/128, 1), stream, x, wr, sc, zr, out, wksp, counters, M, N, K); return out; }
if (dbg == 3) { launch_v4<1, 4, 4, 3>(dim3(4, N/128, 1), stream, x, wr, sc, zr, out, wksp, counters, M, N, K); return out; }
if (variant == 1 && splitk == 4) { LAUNCH(1, 4, 4); }
else if (variant == 1 && splitk == 8) { LAUNCH(1, 8, 4); }
else if (variant == 1 && splitk == 16) { LAUNCH(1, 16, 3); }
else if (variant == 1 && splitk == 2) { LAUNCH(1, 2, 4); }
else if (variant == 1 && splitk == 1) { LAUNCH(1, 1, 4); }
else if (variant == 2 && splitk == 4) { LAUNCH(2, 4, 4); }
else if (variant == 2 && splitk == 2) { LAUNCH(2, 2, 4); }
else if (variant == 2 && splitk == 1) { LAUNCH(2, 1, 3); }
else if (variant == 2 && splitk == 8) { LAUNCH(2, 8, 3); }
else if (variant == 4 && splitk == 1) { LAUNCH(4, 1, 3); }
else if (variant == 4 && splitk == 2) { LAUNCH(4, 2, 3); }
else { TORCH_CHECK(false, "no such variant"); }
return out;
#undef LAUNCH
}
'''
_EXT = None
def _build_ext():
global _EXT
if _EXT is None:
from torch.utils.cpp_extension import load_inline
_EXT = load_inline(
name="w4a16_ext_v6",
cpp_sources=[_CPP_DECL],
cuda_sources=[_GEMV_SRC, _MMA_SRC],
functions=["repack", "gemv_w4", "w4_mma"],
extra_cuda_cflags=["-O3", "--use_fast_math", "-gencode=arch=compute_100,code=sm_100"],
verbose=False,
)
return _EXT
# ----------------------------------------------------------------------------
# Triton fused-dequant fallback (odd shapes)
# ----------------------------------------------------------------------------
import triton
import triton.language as tl
@triton.jit
def _w4_gemm_kernel(
x_ptr, w_ptr, s_ptr, z_ptr, o_ptr,
M, N, K, stride_xm, stride_wn, stride_om,
BM: tl.constexpr, BN: tl.constexpr, BK: tl.constexpr,
):
pid_m = tl.program_id(0)
pid_n = tl.program_id(1)
offs_m = pid_m * BM + tl.arange(0, BM)
offs_n = pid_n * BN + tl.arange(0, BN)
acc = tl.zeros((BM, BN), dtype=tl.float32)
m_mask = offs_m < M
offs_ke = tl.arange(0, BK // 2)
offs_pk = tl.arange(0, BK // 2)
for kb in range(0, K // BK):
x_even = tl.load(x_ptr + offs_m[:, None] * stride_xm + (kb * BK + 2 * offs_ke)[None, :],
mask=m_mask[:, None], other=0.0)
x_odd = tl.load(x_ptr + offs_m[:, None] * stride_xm + (kb * BK + 2 * offs_ke + 1)[None, :],
mask=m_mask[:, None], other=0.0)
wpt = tl.load(w_ptr + (kb * BK // 2 + offs_pk)[:, None] * stride_wn + offs_n[None, :])
lo = (wpt & 0xF).to(tl.bfloat16)
hi = (wpt >> 4).to(tl.bfloat16)
g = (kb * BK) // 128
s = tl.load(s_ptr + g * N + offs_n)
z = tl.load(z_ptr + g * N + offs_n)
lo = (lo - z) * s
hi = (hi - z) * s
acc = tl.dot(x_even, lo, acc)
acc = tl.dot(x_odd, hi, acc)
o_ptrs = o_ptr + offs_m[:, None] * stride_om + offs_n[None, :]
tl.store(o_ptrs, acc.to(tl.bfloat16), mask=m_mask[:, None])
def _triton_w4_gemm(x, wq, sc, zr):
M, K = x.shape
N = wq.shape[1]
out = torch.empty((M, N), dtype=torch.bfloat16, device=x.device)
BM = 16 if M <= 16 else (32 if M <= 32 else 64)
BN, BK, warps, stages = 128, 128, 4, 4
grid = (triton.cdiv(M, BM), triton.cdiv(N, BN))
_w4_gemm_kernel[grid](x, wq, sc, zr, out, M, N, K, x.stride(0), wq.stride(0), out.stride(0),
BM=BM, BN=BN, BK=BK, num_warps=warps, num_stages=stages)
return out
# ----------------------------------------------------------------------------
# Model
# ----------------------------------------------------------------------------
class Model(nn.Module):
def __init__(self, M: int, N: int, K: int, group_size: int = GROUP_SIZE):
super().__init__()
assert K % group_size == 0 and K % 2 == 0
self.M, self.N, self.K = M, N, K
self.group_size = group_size
n_groups = K // group_size
self.register_buffer("w_q", torch.zeros(K // 2, N, dtype=torch.uint8))
self.register_buffer("scales", torch.zeros(n_groups, N, dtype=torch.bfloat16))
self.register_buffer("zeros", torch.zeros(n_groups, N, dtype=torch.bfloat16))
self._wr = None
self._g_wksp = None
self._g_counters = None
self._m_wksp = None
self._m_counters = None
self._device = None
# -- weights repack hook --------------------------------------------------
def load_state_dict(self, *args, **kwargs):
ret = super().load_state_dict(*args, **kwargs)
self._invalidate_repack()
return ret
def _invalidate_repack(self):
self._wr = None # rebuilt lazily on next forward
def _plan(self, x):
"""Kernel dispatch config per (M, N). Returns (path, ...)."""
M, N, K = x.shape[0], self.N, self.K
if K % 2 or self.N % 128 or (self.K // self.group_size) != 32:
return ("triton",)
if M == 1 and N % 512 == 0:
# gemv path: (ksplit, gs, wpg, unroll)
if N >= 12288:
return ("gemv", (16, 2, 2, 8))
return ("gemv", (32, 1, 4, 8))
if N % 128 == 0:
if M <= 16:
return ("mma", 1, 4 if N >= 12288 else 8)
if M <= 32:
return ("mma", 2, 4 if N >= 12288 else 8)
return ("mma", 4, 1)
return ("triton",)
def _ensure(self, x):
dev = x.device
ext = _build_ext()
if self._device != dev:
self._device = dev
self._wr = None
self._g_wksp = None
self._g_counters = None
self._m_wksp = None
self._m_counters = None
if self._wr is None and self.N % 128 == 0 and self.w_q.is_cuda:
self._wr = ext.repack(self.w_q)
return ext
def forward(self, x: torch.Tensor) -> torch.Tensor:
return self._run(x)
# thin __call__ to skip nn.Module hook machinery in the hot path
__call__ = None # replaced below
def _run(self, x: torch.Tensor) -> torch.Tensor:
ext = self._ensure(x)
M = x.shape[0]
N, K = self.N, self.K
plan = self._plan(x)
kind = plan[0]
if kind == "gemv":
ksplit, gs, wpg, unroll = plan[1]
if self._g_wksp is None:
self._g_wksp = torch.zeros(N, dtype=torch.float32, device=x.device)
self._g_counters = torch.zeros(N // 512, dtype=torch.int32, device=x.device)
return ext.gemv_w4(x.view(-1), self.w_q, self.scales, self.zeros,
self._g_wksp, self._g_counters, ksplit, gs, wpg, unroll).view(1, N)
if kind == "mma":
mt, ks = plan[1], plan[2]
mblocks = (M + 16 * mt - 1) // (16 * mt)
if self._m_wksp is None:
self._m_wksp = torch.zeros(32 * M, N, dtype=torch.float32, device=x.device) if ks > 1 else torch.zeros(1, N, dtype=torch.float32, device=x.device)
self._m_counters = torch.zeros(max(1, mblocks) * (N // 128), dtype=torch.int32, device=x.device)
return ext.w4_mma(x, self._wr, self.scales, self.zeros,
self._m_wksp, self._m_counters, mt, ks, 0)
return _triton_w4_gemm(x, self.w_q, self.scales, self.zeros)
def _model_call(self, x):
return self._run(x)
Model.__call__ = _model_call
M = 1
N = 12288
K = 4096
def get_inputs():
x = torch.randn(M, K, dtype=torch.bfloat16)
return [x]
def get_init_inputs():
return [M, N, K]
20260715_220829_kinetic-claude_kinetic-0715_07_w4a16_gemm