KernelBench hard · RTX PRO 6000
Paged Attention Grok 4.6
62.2%geomean peak fraction across shapes
manually audited: clean
load_inline CUDA paged-attention decode (cp.async, split-K, GQA). CUDA-graph keyed on query/KV/block_table/seq_lens data_ptr plus shapes; recaptures on a new key. template_mutated=false.
harnessgrokagent session1h 22mtotal wall1h 23mcheck15sbenchmark4soutput tokens—regimememory
Per-shape vs governing ceilingeach shape graded against whichever binds — bf16 compute or HBM bandwidth
8×32×8×128×1024×160.032 ms58.7%1.06 TB/s · 59% of 1.8 TB/s HBM · also 4 TFLOPS (1% of compute)
32×32×8×128×2048×160.184 ms81.2%1.46 TB/s · 81% of 1.8 TB/s HBM · also 6 TFLOPS (1% of compute)
4×64×8×128×4096×160.058 ms64.1%1.15 TB/s · 64% of 1.8 TB/s HBM · also 9 TFLOPS (2% of compute)
16×32×8×128×1535×160.077 ms72.4%1.30 TB/s · 72% of 1.8 TB/s HBM · also 5 TFLOPS (1% of compute)
8×16×4×64×2000×160.022 ms42.0%0.76 TB/s · 42% of 1.8 TB/s HBM · also 3 TFLOPS (1% of compute)
compute-bound memory-bound · bar + right column = official fraction of the ceiling (the geomean input)
geomean(58.7% · 81.2% · 64.1% · 72.4% · 42.0%) = 62.2%
Kernel source (redacted)
"""Paged-attention decode: GQA-fused CUDA kernel (cp.async, split-K, CUDA graph)."""
import math
import os
from pathlib import Path
import torch
import torch.nn as nn
BATCH = 8
NUM_HEADS = 32
NUM_KV_HEADS = 8
HEAD_DIM = 128
SEQ_LEN = 1024
PAGE_SIZE = 16
_mod = None
_ws_cache: dict = {}
CPP_SRC = r'''
#include <torch/extension.h>
#include <c10/cuda/CUDAStream.h>
#include <cuda_runtime_api.h>
extern "C" void paged_decode_launch(
const void* q, const void* kv, const int* block_table, const int* seq_lens,
void* out, float* tmp_out, float* tmp_m, float* tmp_l,
int B, int H, int Hkv, int D, int P, int max_pages, int splits,
float scale, cudaStream_t stream);
void paged_decode(
torch::Tensor q, torch::Tensor kv, torch::Tensor block_table, torch::Tensor seq_lens,
torch::Tensor out, torch::Tensor tmp_out, torch::Tensor tmp_m, torch::Tensor tmp_l,
int64_t num_kv_heads, int64_t page_size, int64_t num_splits, double scale)
{
const int B = (int)q.size(0);
const int H = (int)q.size(1);
const int D = (int)q.size(2);
paged_decode_launch(
q.data_ptr(), kv.data_ptr(),
block_table.data_ptr<int>(), seq_lens.data_ptr<int>(),
out.data_ptr(),
tmp_out.defined() && tmp_out.numel() > 0 ? tmp_out.data_ptr<float>() : nullptr,
tmp_m.defined() && tmp_m.numel() > 0 ? tmp_m.data_ptr<float>() : nullptr,
tmp_l.defined() && tmp_l.numel() > 0 ? tmp_l.data_ptr<float>() : nullptr,
B, H, (int)num_kv_heads, D, (int)page_size, (int)block_table.size(1),
(int)num_splits, (float)scale, c10::cuda::getCurrentCUDAStream());
}
'''
def _write_sources(root: Path) -> list[str]:
cu = root / "_paged_attn.cu"
cpp = root / "_paged_attn.cpp"
cu_text = CUDA_SRC
cpp_text = CPP_SRC + """
PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
m.def("paged_decode", &paged_decode);
}
"""
if not cu.exists() or cu.read_text() != cu_text:
cu.write_text(cu_text)
if not cpp.exists() or cpp.read_text() != cpp_text:
cpp.write_text(cpp_text)
return [str(cpp), str(cu)]
def _get_mod():
global _mod
if _mod is not None:
return _mod
os.environ["TORCH_CUDA_ARCH_LIST"] = "12.0"
from torch.utils.cpp_extension import load
root = Path(__file__).resolve().parent
sources = _write_sources(root)
_mod = load(
name="paged_attn_decode_v2clean",
sources=sources,
extra_cuda_cflags=[
"-O3",
"--use_fast_math",
"-lineinfo",
"-std=c++17",
"-U__CUDA_NO_HALF_OPERATORS__",
"-U__CUDA_NO_BFLOAT16_OPERATORS__",
"-U__CUDA_NO_BFLOAT16_CONVERSIONS__",
],
extra_ldflags=["-lcuda"],
verbose=False,
)
return _mod
def _choose_splits(batch: int, num_kv_heads: int, seq_len: int, page_size: int, head_dim: int = 128) -> int:
n_pages = (seq_len + page_size - 1) // page_size
n_groups = max(1, batch * num_kv_heads)
# Hand-tuned for the five eval shapes (CUDA-graph + this kernel).
tuned = {
(8, 8, 1024, 128): 8,
(32, 8, 2048, 128): 3,
(4, 8, 4096, 128): 24,
(16, 8, 1535, 128): 6,
(8, 4, 2000, 64): 24,
}
hit = tuned.get((batch, num_kv_heads, seq_len, head_dim))
if hit is not None:
return min(int(hit), n_pages)
kv_bytes = 2 * batch * seq_len * num_kv_heads * head_dim * 2
target = 768
splits = max(1, (target + n_groups - 1) // n_groups)
splits = min(splits, n_pages)
min_pps = 2 if kv_bytes < 40_000_000 else 4
splits = min(splits, max(1, n_pages // min_pps))
return int(splits)
def _workspace(batch, num_heads, splits, head_dim, device):
key = (batch, num_heads, splits, head_dim, str(device))
ws = _ws_cache.get(key)
if ws is None:
ws = (
torch.empty(batch, num_heads, splits, head_dim, dtype=torch.float32, device=device),
torch.empty(batch, num_heads, splits, dtype=torch.float32, device=device),
torch.empty(batch, num_heads, splits, dtype=torch.float32, device=device),
)
_ws_cache[key] = ws
return ws
class Model(nn.Module):
def __init__(
self,
batch: int,
num_heads: int,
num_kv_heads: int,
head_dim: int,
seq_len: int,
page_size: int,
):
super().__init__()
assert num_heads % num_kv_heads == 0
self.batch = batch
self.num_heads = num_heads
self.num_kv_heads = num_kv_heads
self.head_dim = head_dim
self.seq_len = seq_len
self.page_size = page_size
self.group_size = num_heads // num_kv_heads
self.scale = 1.0 / math.sqrt(head_dim)
self.register_buffer("_dummy", torch.zeros(1, dtype=torch.bfloat16), persistent=False)
self._splits = _choose_splits(batch, num_kv_heads, seq_len, page_size, head_dim)
self._graph = None
self._graph_key = None
self._out = None
self._dummy_tmp = None
def _launch(self, query, kv_cache, block_table, seq_lens, out):
B, H, D = query.shape
splits = self._splits
if splits > 1:
tmp_out, tmp_m, tmp_l = _workspace(B, H, splits, D, query.device)
else:
if self._dummy_tmp is None:
self._dummy_tmp = torch.empty(0, dtype=torch.float32, device=query.device)
tmp_out = tmp_m = tmp_l = self._dummy_tmp
_get_mod().paged_decode(
query, kv_cache, block_table, seq_lens, out,
tmp_out, tmp_m, tmp_l,
int(self.num_kv_heads), int(self.page_size), int(splits), float(self.scale),
)
def forward(self, query, kv_cache, block_table, seq_lens):
key = (
query.data_ptr(), kv_cache.data_ptr(),
block_table.data_ptr(), seq_lens.data_ptr(),
tuple(query.shape), tuple(kv_cache.shape),
)
if key != self._graph_key:
self._out = torch.empty_like(query)
self._launch(query, kv_cache, block_table, seq_lens, self._out)
torch.cuda.synchronize()
g = torch.cuda.CUDAGraph()
try:
with torch.cuda.graph(g):
self._launch(query, kv_cache, block_table, seq_lens, self._out)
self._graph = g
self._graph_key = key
except Exception:
self._graph = None
self._graph_key = None
return self._out
self._graph.replay()
return self._out
def get_inputs():
B, H, Hkv, D, L, P = BATCH, NUM_HEADS, NUM_KV_HEADS, HEAD_DIM, SEQ_LEN, PAGE_SIZE
pages_per_seq = (L + P - 1) // P
total_pages = max(B * pages_per_seq + 8, 64)
query = torch.randn(B, H, D, dtype=torch.bfloat16) * 0.1
kv_cache = torch.randn(total_pages, P, Hkv, 2 * D, dtype=torch.bfloat16) * 0.1
perm = torch.randperm(total_pages)[: B * pages_per_seq].reshape(B, pages_per_seq).int()
block_table = perm.contiguous()
seq_lens = torch.full((B,), L, dtype=torch.int32)
return [query, kv_cache, block_table, seq_lens]
def get_init_inputs():
return [BATCH, NUM_HEADS, NUM_KV_HEADS, HEAD_DIM, SEQ_LEN, PAGE_SIZE]
CUDA_SRC = r'''#include <cuda_bf16.h>
#include <cuda_runtime.h>
#include <cfloat>
#include <cstdint>
using bf16 = __nv_bfloat16;
#define DEVICE __device__ __forceinline__
DEVICE uint2 ldca_u64(const void* p) {
uint2 v;
asm volatile("ld.global.ca.v2.u32 {%0, %1}, [%2];" : "=r"(v.x), "=r"(v.y) : "l"(p));
return v;
}
DEVICE unsigned ldca_u32(const void* p) {
unsigned v;
asm volatile("ld.global.ca.b32 %0, [%1];" : "=r"(v) : "l"(p));
return v;
}
DEVICE void u32_to_f2(unsigned u, float& a, float& b) {
__nv_bfloat162 x = *reinterpret_cast<const __nv_bfloat162*>(&u);
float2 f = __bfloat1622float2(x);
a = f.x; b = f.y;
}
DEVICE unsigned f2_to_u32(float a, float b) {
__nv_bfloat162 x = __floats2bfloat162_rn(a, b);
return *reinterpret_cast<const unsigned*>(&x);
}
DEVICE void st_u64(void* p, uint2 v) {
asm volatile("st.global.v2.u32 [%0], {%1, %2};" :: "l"(p), "r"(v.x), "r"(v.y));
}
DEVICE float warp_sum(float x) {
#pragma unroll
for (int m = 16; m > 0; m >>= 1)
x += __shfl_xor_sync(0xffffffff, x, m);
return x;
}
DEVICE void cp_async_16(void* smem_dst, const void* glob_src) {
unsigned smem_ptr = __cvta_generic_to_shared(smem_dst);
asm volatile("cp.async.cg.shared.global.L2::128B [%0], [%1], 16;\n"
:: "r"(smem_ptr), "l"(glob_src));
}
DEVICE void cp_async_commit() { asm volatile("cp.async.commit_group;\n" ::); }
template <int N>
DEVICE void cp_async_wait() { asm volatile("cp.async.wait_group %0;\n" :: "n"(N)); }
template <int HEAD_DIM, int PAGE_SIZE>
DEVICE void load_page_async(bf16* smem, const bf16* page_kv_head, int64_t tok_stride, int tid, int nthreads) {
constexpr int BYTES_PER_TOK = 2 * HEAD_DIM * (int)sizeof(bf16);
constexpr int CHUNKS_PER_TOK = BYTES_PER_TOK / 16;
constexpr int TOTAL_CHUNKS = PAGE_SIZE * CHUNKS_PER_TOK;
#pragma unroll
for (int i = tid; i < TOTAL_CHUNKS; i += nthreads) {
int tok = i / CHUNKS_PER_TOK;
int chunk = i % CHUNKS_PER_TOK;
const char* src = reinterpret_cast<const char*>(page_kv_head + (int64_t)tok * tok_stride) + chunk * 16;
char* dst = reinterpret_cast<char*>(smem) + (tok * BYTES_PER_TOK + chunk * 16);
cp_async_16(dst, src);
}
}
template <int HEAD_DIM, int GROUP_SIZE, int NUM_WARPS, int PAGE_SIZE>
__global__ void paged_decode_kernel(
const bf16* __restrict__ q,
const bf16* __restrict__ kv,
const int* __restrict__ block_table,
const int* __restrict__ seq_lens,
bf16* __restrict__ out,
float* __restrict__ tmp_out,
float* __restrict__ tmp_m,
float* __restrict__ tmp_l,
int max_pages,
int num_kv_heads,
int num_heads,
int num_splits,
float scale)
{
constexpr int WARP = 32;
constexpr int ELEMS = HEAD_DIM / WARP;
constexpr int HEADS_PER_WARP = GROUP_SIZE / NUM_WARPS;
constexpr int KV_ELEMS = 2 * HEAD_DIM;
constexpr int SMEM_PAGE = PAGE_SIZE * KV_ELEMS;
const int kv_head = blockIdx.x;
const int batch = blockIdx.y;
const int split = blockIdx.z;
const int tid = threadIdx.x;
const int warp = tid / WARP;
const int lane = tid % WARP;
const int seq_len = seq_lens[batch];
const int num_pages = (seq_len + PAGE_SIZE - 1) / PAGE_SIZE;
const int pages_per_split = (num_pages + num_splits - 1) / num_splits;
const int page_begin = split * pages_per_split;
const int page_end = min(page_begin + pages_per_split, num_pages);
const int q_head0 = kv_head * GROUP_SIZE + warp * HEADS_PER_WARP;
const int64_t tok_stride = (int64_t)num_kv_heads * KV_ELEMS;
const int64_t page_stride = (int64_t)PAGE_SIZE * tok_stride;
extern __shared__ bf16 smem[];
bf16* buf0 = smem;
bf16* buf1 = smem + SMEM_PAGE;
float q_reg[HEADS_PER_WARP][ELEMS];
float acc[HEADS_PER_WARP][ELEMS];
float mstat[HEADS_PER_WARP];
float lstat[HEADS_PER_WARP];
#pragma unroll
for (int h = 0; h < HEADS_PER_WARP; ++h) {
mstat[h] = -FLT_MAX;
lstat[h] = 0.f;
#pragma unroll
for (int i = 0; i < ELEMS; ++i) acc[h][i] = 0.f;
const bf16* qp = q + ((int64_t)batch * num_heads + q_head0 + h) * HEAD_DIM + lane * ELEMS;
if constexpr (ELEMS == 4) {
uint2 u = ldca_u64(qp);
u32_to_f2(u.x, q_reg[h][0], q_reg[h][1]);
u32_to_f2(u.y, q_reg[h][2], q_reg[h][3]);
} else {
unsigned u = ldca_u32(qp);
u32_to_f2(u, q_reg[h][0], q_reg[h][1]);
}
#pragma unroll
for (int i = 0; i < ELEMS; ++i) q_reg[h][i] *= scale;
}
auto consume_page = [&](const bf16* page_smem, int token_base) {
#pragma unroll
for (int t = 0; t < PAGE_SIZE; ++t) {
const bool valid = (token_base + t) < seq_len;
const bf16* kptr = page_smem + t * KV_ELEMS + lane * ELEMS;
const bf16* vptr = page_smem + t * KV_ELEMS + HEAD_DIM + lane * ELEMS;
float k[ELEMS], v[ELEMS];
if constexpr (ELEMS == 4) {
uint2 uk = *reinterpret_cast<const uint2*>(kptr);
uint2 uv = *reinterpret_cast<const uint2*>(vptr);
u32_to_f2(uk.x, k[0], k[1]); u32_to_f2(uk.y, k[2], k[3]);
u32_to_f2(uv.x, v[0], v[1]); u32_to_f2(uv.y, v[2], v[3]);
} else {
unsigned uk = *reinterpret_cast<const unsigned*>(kptr);
unsigned uv = *reinterpret_cast<const unsigned*>(vptr);
u32_to_f2(uk, k[0], k[1]);
u32_to_f2(uv, v[0], v[1]);
}
#pragma unroll
for (int h = 0; h < HEADS_PER_WARP; ++h) {
float qk = 0.f;
#pragma unroll
for (int i = 0; i < ELEMS; ++i) qk += q_reg[h][i] * k[i];
qk = warp_sum(qk);
if (valid) {
float m_new = fmaxf(mstat[h], qk);
float a = __expf(mstat[h] - m_new);
float p = __expf(qk - m_new);
lstat[h] = lstat[h] * a + p;
#pragma unroll
for (int i = 0; i < ELEMS; ++i)
acc[h][i] = acc[h][i] * a + p * v[i];
mstat[h] = m_new;
}
}
}
};
if (page_begin < page_end) {
const int* bt = block_table + (int64_t)batch * max_pages;
int phys = bt[page_begin];
const bf16* page0 = kv + (int64_t)phys * page_stride + (int64_t)kv_head * KV_ELEMS;
load_page_async<HEAD_DIM, PAGE_SIZE>(buf0, page0, tok_stride, tid, NUM_WARPS * WARP);
cp_async_commit();
int cur = 0;
for (int page = page_begin; page < page_end; ++page) {
const int nxt = page + 1;
if (nxt < page_end) {
int nphys = bt[nxt];
const bf16* pagen = kv + (int64_t)nphys * page_stride + (int64_t)kv_head * KV_ELEMS;
bf16* nbuf = (cur == 0) ? buf1 : buf0;
load_page_async<HEAD_DIM, PAGE_SIZE>(nbuf, pagen, tok_stride, tid, NUM_WARPS * WARP);
cp_async_commit();
cp_async_wait<1>();
} else {
cp_async_wait<0>();
}
__syncthreads();
consume_page((cur == 0) ? buf0 : buf1, page * PAGE_SIZE);
__syncthreads();
cur ^= 1;
}
}
const bool single = (num_splits == 1);
#pragma unroll
for (int h = 0; h < HEADS_PER_WARP; ++h) {
const int head = q_head0 + h;
if (single) {
float inv = (lstat[h] > 0.f) ? __fdividef(1.f, lstat[h]) : 0.f;
bf16* op = out + ((int64_t)batch * num_heads + head) * HEAD_DIM + lane * ELEMS;
if constexpr (ELEMS == 4) {
uint2 u;
u.x = f2_to_u32(acc[h][0] * inv, acc[h][1] * inv);
u.y = f2_to_u32(acc[h][2] * inv, acc[h][3] * inv);
st_u64(op, u);
} else {
*reinterpret_cast<unsigned*>(op) = f2_to_u32(acc[h][0] * inv, acc[h][1] * inv);
}
} else {
float* op = tmp_out +
((((int64_t)batch * num_heads + head) * num_splits + split) * HEAD_DIM) +
lane * ELEMS;
#pragma unroll
for (int i = 0; i < ELEMS; ++i) op[i] = acc[h][i];
if (lane == 0) {
int64_t sidx = ((int64_t)batch * num_heads + head) * num_splits + split;
tmp_m[sidx] = mstat[h];
tmp_l[sidx] = lstat[h];
}
}
}
}
template <int HEAD_DIM>
__global__ void paged_reduce_kernel(
const float* __restrict__ tmp_out,
const float* __restrict__ tmp_m,
const float* __restrict__ tmp_l,
bf16* __restrict__ out,
int num_heads,
int num_splits)
{
const int head = blockIdx.x;
const int batch = blockIdx.y;
const int tid = threadIdx.x;
const float* m_ptr = tmp_m + ((int64_t)batch * num_heads + head) * num_splits;
const float* l_ptr = tmp_l + ((int64_t)batch * num_heads + head) * num_splits;
const float* o_ptr = tmp_out + ((int64_t)batch * num_heads + head) * num_splits * HEAD_DIM;
float m_g = -FLT_MAX;
for (int s = 0; s < num_splits; ++s) m_g = fmaxf(m_g, m_ptr[s]);
float acc = 0.f;
float l_g = 0.f;
for (int s = 0; s < num_splits; ++s) {
float a = (m_ptr[s] == -FLT_MAX) ? 0.f : __expf(m_ptr[s] - m_g);
if (tid == 0) l_g += l_ptr[s] * a;
if (tid < HEAD_DIM) acc += o_ptr[s * HEAD_DIM + tid] * a;
}
__shared__ float sm_l;
if (tid == 0) sm_l = l_g;
__syncthreads();
if (tid < HEAD_DIM) {
float inv = (sm_l > 0.f) ? __fdividef(1.f, sm_l) : 0.f;
out[((int64_t)batch * num_heads + head) * HEAD_DIM + tid] = __float2bfloat16(acc * inv);
}
}
static void launch_decode(
const bf16* q, const bf16* kv, const int* bt, const int* sl,
bf16* out, float* tmp_out, float* tmp_m, float* tmp_l,
int B, int H, int Hkv, int D, int P, int max_pages, int splits,
float scale, cudaStream_t stream)
{
const int G = H / Hkv;
dim3 grid(Hkv, B, splits);
auto smem_for = [](int D, int page) {
return (size_t)2 * page * 2 * D * sizeof(bf16);
};
if (D == 128 && G == 4 && P == 16) {
constexpr int WARPS = 4;
paged_decode_kernel<128, 4, WARPS, 16>
<<<grid, WARPS * 32, smem_for(128, 16), stream>>>(
q, kv, bt, sl, out, tmp_out, tmp_m, tmp_l,
max_pages, Hkv, H, splits, scale);
} else if (D == 128 && G == 8 && P == 16) {
constexpr int WARPS = 8;
paged_decode_kernel<128, 8, WARPS, 16>
<<<grid, WARPS * 32, smem_for(128, 16), stream>>>(
q, kv, bt, sl, out, tmp_out, tmp_m, tmp_l,
max_pages, Hkv, H, splits, scale);
} else if (D == 64 && G == 4 && P == 16) {
constexpr int WARPS = 4;
paged_decode_kernel<64, 4, WARPS, 16>
<<<grid, WARPS * 32, smem_for(64, 16), stream>>>(
q, kv, bt, sl, out, tmp_out, tmp_m, tmp_l,
max_pages, Hkv, H, splits, scale);
}
}
extern "C" void paged_decode_launch(
const void* q, const void* kv, const int* block_table, const int* seq_lens,
void* out, float* tmp_out, float* tmp_m, float* tmp_l,
int B, int H, int Hkv, int D, int P, int max_pages, int splits,
float scale, cudaStream_t stream)
{
launch_decode(
reinterpret_cast<const bf16*>(q), reinterpret_cast<const bf16*>(kv),
block_table, seq_lens, reinterpret_cast<bf16*>(out),
tmp_out, tmp_m, tmp_l,
B, H, Hkv, D, P, max_pages, splits, scale, stream);
if (splits > 1) {
dim3 grid(H, B);
dim3 block(D > 32 ? D : 32);
if (D == 128)
paged_reduce_kernel<128><<<grid, block, 0, stream>>>(
tmp_out, tmp_m, tmp_l, reinterpret_cast<bf16*>(out), H, splits);
else if (D == 64)
paged_reduce_kernel<64><<<grid, block, 0, stream>>>(
tmp_out, tmp_m, tmp_l, reinterpret_cast<bf16*>(out), H, splits);
}
}
'''
20260813_072847_grok_grok-4.6_03_paged_attention