KernelBench cuda · RTX PRO 6000
DeepSeek NSA GLM-5.3 Flash
4.45%geomean peak fraction across shapes
manually audited: clean
Isolated regrade 0.0445 (in-run 0.0444). Hand-written CUDA DeepSeek Native Sparse Attention: prefix-sum block keys, fused top-8 plus last-64 window, online softmax over the union. Language gate cuda_raw. No graph, no output cache, no foreign archive. template_mutated=false.
harnessor-fableagent session3h 31mtotal wall3h 31mcheck3sbenchmark2soutput tokens—regimecompute
Per-shape vs governing ceilingeach shape graded against whichever binds — bf16 compute or HBM bandwidth
1×16×2048×641.596 ms2.1%11 TFLOPS · 2% of 500 TF bf16 peak · also 0.01 TB/s (1% of HBM)
1×16×4127×642.139 ms6.5%33 TFLOPS · 7% of 500 TF bf16 peak · also 0.02 TB/s (1% of HBM)
1×8×8192×642.289 ms12.0%60 TFLOPS · 12% of 500 TF bf16 peak · also 0.01 TB/s (1% of HBM)
1×8×8191×1285.574 ms9.9%49 TFLOPS · 10% of 500 TF bf16 peak · also 0.01 TB/s (1% of HBM)
4×8×1024×641.436 ms1.2%6 TFLOPS · 1% of 500 TF bf16 peak · also 0.01 TB/s (1% of HBM)
2×8×3000×641.884 ms3.9%20 TFLOPS · 4% of 500 TF bf16 peak · also 0.01 TB/s (1% of HBM)
compute-bound memory-bound · bar + right column = official fraction of the ceiling (the geomean input)
geomean(2.1% · 6.5% · 12.0% · 9.9% · 1.2% · 3.9%) = 4.5%
Kernel source (redacted)
"""DeepSeek NSA-inspired sparse attention — hand-written CUDA for RTX PRO 6000 (SM120).
Bench semantics (identical to reference.nsa_attend):
per query t: score keys causally, rank 64-key blocks by mean score, take top-8,
union with the last-64-token sliding window, softmax-attend over that key set.
Implementation notes
--------------------
Block importance = mean_{j in block, j<=t} (q_t.k_j)/sqrt(D)
= q_t . (sum_{j} k_j) / cnt / sqrt(D),
so scoring needs one dot per (query, block) against a *precomputed block key
prefix-sum* table instead of O(S) per-query work:
P[bh, bi, m, :] = sum_{j<=m} k[bi*64+j] (fp32)
Fully-causal blocks use m=63; the diagonal block uses m = t - bi*64 (causal
prefix), which reproduces the reference's causal-only mean exactly.
Kernel A: builds P (coalesced fp32 writes, one CTA per (bh, block)).
Kernel B: fused select + sparse attention, one CTA per 128 queries of a (b,h),
one warp per query (small shared footprint -> many CTAs per SM, which matters
because the kernel is latency-bound):
- stage Q as fp32 in shared memory
- phase 1: per-query importance dots against P rows -> shared scratch,
iterative warp argmax extracts the top-8 block ids
- sliding-window blocks merge in; entries coming only from the window are
clipped to start at w0 so the attended key set equals the reference union
exactly (no double counting when a window block is also a top block)
- phase 3: online (streaming) softmax over the <=10 chosen segments; QK dots
are lane-per-key with full-row uint4 bf16 loads, PV keeps two independent
FMA accumulator chains per dim pair (latency hiding), probs broadcast
through shuffles
Everything accumulates in fp32 from the same bf16 inputs the oracle casts to
fp32, so results track the reference well inside the 0.1 tolerance.
"""
from __future__ import annotations
import math
import torch
import torch.nn as nn
from torch.utils.cpp_extension import load_inline
OP_TYPE = "deepseek_nsa"
SUPPORTED_PRECISIONS = ["bf16"]
HARDWARE_REQUIRED = ["RTX_PRO_6000"]
B, H, S, D = 1, 16, 1024, 64
BLOCK_SIZE = 64
TOP_N_BLOCKS = 8
SLIDING_WINDOW = 64
_CUDA_SRC = r"""
#include <torch/extension.h>
#include <ATen/cuda/CUDAContext.h>
#include <cuda_bf16.h>
#include <cmath>
#define NWA 8 // warps per CTA
#define TPQ 128 // queries per CTA
#define BSZ 64 // NSA block size
#define TOPN 8 // blocks kept by importance
#define WINSZ 64 // sliding window
#define MAXSEL (TOPN + 2)
// ---------------------------------------------------------------------------
// Kernel A: block key prefix sums.
// P[(bh*NB + bi)*BSZ*D + m*D + d] = sum_{j<=m} K[bh, bi*BSZ+j, d]
// ---------------------------------------------------------------------------
__global__ void nsa_prefix_kernel(const __nv_bfloat16* __restrict__ K,
float* __restrict__ P,
int S, int D, int NB, int BH)
{
const int bi = blockIdx.x;
const int bh = blockIdx.y;
const int d = threadIdx.x;
if (d >= D) return;
const int valid = min(BSZ, S - bi * BSZ);
const __nv_bfloat16* kbase = K + ((size_t)bh * S + (size_t)bi * BSZ) * D;
float* pbase = P + ((size_t)(bh * NB + bi) * BSZ) * D;
float acc = 0.f;
for (int m = 0; m < BSZ; ++m) {
if (m < valid) acc += __bfloat162float(kbase[(size_t)m * D + d]);
pbase[(size_t)m * D + d] = acc;
}
}
// ---------------------------------------------------------------------------
// Warp reductions (full-warp, converged call sites only).
// ---------------------------------------------------------------------------
__device__ __forceinline__ float warp_sum(float v) {
#pragma unroll
for (int o = 16; o > 0; o >>= 1) v += __shfl_xor_sync(0xffffffffu, v, o);
return v;
}
__device__ __forceinline__ float warp_max(float v) {
#pragma unroll
for (int o = 16; o > 0; o >>= 1) v = fmaxf(v, __shfl_xor_sync(0xffffffffu, v, o));
return v;
}
// Arg-max keeping the smallest index on ties; result replicated on all lanes.
__device__ __forceinline__ void warp_argmin_idx(float v, int idx, float& bv, int& bidx) {
#pragma unroll
for (int o = 16; o > 0; o >>= 1) {
const float ov = __shfl_xor_sync(0xffffffffu, v, o);
const int oi = __shfl_xor_sync(0xffffffffu, idx, o);
if (ov > v || (ov == v && oi < idx)) { v = ov; idx = oi; }
}
bv = v; bidx = idx;
}
// ---------------------------------------------------------------------------
// Kernel B: fused importance scoring + top-n select + sparse attention.
// GC = ceil(D/64): each lane owns the dim pair {g*64 + 2*lane, +1} within every
// 64-dim half, so all D dims are covered exactly once (guards trim odd tails).
//
// Shared layout (floats): q_sm[NWA*D] | imp[NWA*sp], sp = NB + 1 (padding
// keeps per-warp importance rows bank-spread).
// ---------------------------------------------------------------------------
template <int GC>
__global__ void __launch_bounds__(NWA * 32)
nsa_attend_kernel(const __nv_bfloat16* __restrict__ Q,
const __nv_bfloat16* __restrict__ K,
const __nv_bfloat16* __restrict__ V,
const float* __restrict__ P,
__nv_bfloat16* __restrict__ O,
int S, int D, int NB, float scale)
{
extern __shared__ float smem[];
const int sp = NB + 1;
const int tx = threadIdx.x;
const int wid = tx >> 5;
const int lane = tx & 31;
float* q_sm = smem + (size_t)wid * D; // this warp's query row
float* imp = smem + (size_t)NWA * D + (size_t)wid * sp;
const int bh = blockIdx.y;
const int t0 = blockIdx.x * TPQ;
const size_t bhOff = (size_t)bh * S * D;
const __nv_bfloat16* Kb = K + bhOff;
const __nv_bfloat16* Vb = V + bhOff;
const float* Pb = P + (size_t)bh * NB * BSZ * D;
for (int ti = wid; ti < TPQ; ti += NWA) {
const int t = t0 + ti;
if (t >= S) break;
const size_t qoff = bhOff + (size_t)t * D;
// ---- stage q (fp32) ----
for (int i = lane; i < D; i += 32) q_sm[i] = __bfloat162float(Q[qoff + i]);
__syncwarp();
const int diag = t / BSZ;
const int nv = diag + 1; // blocks 0..diag are causal
const int mdg = t - diag * BSZ + 1; // causal keys in diagonal block
const int w0 = max(0, t + 1 - WINSZ); // sliding window start
const float f_bl = scale / (float)BSZ;
const float f_dg = scale / (float)mdg;
// lane's fixed q values (dim pairs per 64-dim half) for phase 1
// (skipped entirely when every causal block makes the top-N anyway)
if (nv > TOPN) {
float qr[GC][2];
#pragma unroll
for (int g = 0; g < GC; ++g) {
const int d0 = g * 64 + 2 * lane;
qr[g][0] = (d0 < D) ? q_sm[d0] : 0.f;
qr[g][1] = (d0 + 1 < D) ? q_sm[d0 + 1] : 0.f;
}
// ---- phase 1: block importances ----
for (int bi = 0; bi < nv; ++bi) {
const bool full = (bi < diag);
const float* row = Pb + (size_t)bi * BSZ * D
+ (size_t)(full ? BSZ - 1 : mdg - 1) * D;
float a = 0.f;
#pragma unroll
for (int g = 0; g < GC; ++g) {
const int d0 = g * 64 + 2 * lane;
if (d0 < D) {
const float r0 = row[d0];
const float r1 = (d0 + 1 < D) ? row[d0 + 1] : 0.f;
a = fmaf(qr[g][0], r0, a);
a = fmaf(qr[g][1], r1, a);
}
}
imp[bi] = warp_sum(a) * (full ? f_bl : f_dg);
}
__syncwarp(); // imp writes must be visible across lanes before scanning
}
// ---- phase 2: top-N selection ----
// All sel_* indices are unroll-time constants so the arrays live in
// registers instead of spilling to local memory.
int sel_id[MAXSEL];
int sel_lo[MAXSEL];
bool sel_ok[MAXSEL]; // slot holds a real segment
#pragma unroll
for (int i = 0; i < MAXSEL; ++i) sel_ok[i] = false;
if (nv <= TOPN) {
// all causal blocks are selected; scoring would not change the set
#pragma unroll
for (int bi = 0; bi < TOPN; ++bi)
if (bi < nv) { sel_id[bi] = bi; sel_lo[bi] = bi * BSZ; sel_ok[bi] = true; }
} else {
#pragma unroll
for (int kk = 0; kk < TOPN; ++kk) {
float best = -INFINITY; int bidx = lane;
for (int i = lane; i < nv; i += 32) {
const float v = imp[i];
if (v > best) { best = v; bidx = i; }
}
float bv; int bid;
warp_argmin_idx(best, bidx, bv, bid);
sel_id[kk] = bid; sel_lo[kk] = bid * BSZ; sel_ok[kk] = true;
if (lane == 0) imp[bid] = -INFINITY;
__syncwarp();
}
}
// window blocks (clipped to w0 unless already picked as a top block)
const int wb0 = w0 / BSZ;
#pragma unroll
for (int c = 0; c < 2; ++c) {
const int cand = c ? diag : wb0;
sel_id[TOPN + c] = cand;
sel_lo[TOPN + c] = max(cand * BSZ, w0);
bool dup = false;
#pragma unroll
for (int i = 0; i < TOPN + c; ++i) // include earlier window slot
dup = dup || (sel_ok[i] && sel_id[i] == cand);
sel_ok[TOPN + c] = !dup;
}
// ---- phase 3: online softmax over selected segments ----
float m_run = -INFINITY, l_run = 0.f;
float acA[GC][2], acB[GC][2]; // two independent FMA chains
#pragma unroll
for (int g = 0; g < GC; ++g) {
acA[g][0] = 0.f; acA[g][1] = 0.f;
acB[g][0] = 0.f; acB[g][1] = 0.f;
}
#pragma unroll
for (int e = 0; e < MAXSEL; ++e) {
if (!sel_ok[e]) continue;
const int lo = sel_lo[e];
const int hi = min(sel_id[e] * BSZ + BSZ, t + 1);
for (int j0 = lo; j0 < hi; j0 += 32) {
const int jj = j0 + lane;
const bool val = (jj < hi);
float s = -INFINITY;
if (val) {
const uint4* kr = reinterpret_cast<const uint4*>(Kb + (size_t)jj * D);
float s0 = 0.f, s1 = 0.f, s2 = 0.f, s3 = 0.f;
#pragma unroll
for (int c = 0; c < GC * 8; ++c) { // 8 dims per chunk
if (c * 8 < D) {
const uint4 kvu = kr[c];
const __nv_bfloat162* kp = reinterpret_cast<const __nv_bfloat162*>(&kvu);
const float2 fa = __bfloat1622float2(kp[0]);
const float2 fb = __bfloat1622float2(kp[1]);
const float2 fc = __bfloat1622float2(kp[2]);
const float2 fd = __bfloat1622float2(kp[3]);
s0 = fmaf(fa.x, q_sm[c*8+0], s0); s0 = fmaf(fa.y, q_sm[c*8+1], s0);
s1 = fmaf(fb.x, q_sm[c*8+2], s1); s1 = fmaf(fb.y, q_sm[c*8+3], s1);
s2 = fmaf(fc.x, q_sm[c*8+4], s2); s2 = fmaf(fc.y, q_sm[c*8+5], s2);
s3 = fmaf(fd.x, q_sm[c*8+6], s3); s3 = fmaf(fd.y, q_sm[c*8+7], s3);
}
}
s = (s0 + s1 + s2 + s3) * scale;
}
const float mc = warp_max(val ? s : -INFINITY);
const float mn = fmaxf(m_run, mc);
const float alpha = __expf(m_run - mn);
const float p = val ? __expf(s - mn) : 0.f;
l_run = l_run * alpha + warp_sum(p);
m_run = mn;
#pragma unroll
for (int g = 0; g < GC; ++g) {
acA[g][0] *= alpha; acA[g][1] *= alpha;
acB[g][0] *= alpha; acB[g][1] *= alpha;
}
// PV: probs broadcast by shuffle, two keys in flight per step
#pragma unroll
for (int kk = 0; kk < 32; kk += 2) {
const float pk0 = __shfl_sync(0xffffffffu, p, kk);
const float pk1 = __shfl_sync(0xffffffffu, p, kk + 1);
const int ja = j0 + kk, jb = j0 + kk + 1;
#pragma unroll
for (int g = 0; g < GC; ++g) {
const int d0 = g * 64 + 2 * lane;
if (d0 < D) {
if (ja < hi) {
const __nv_bfloat162 vb =
*reinterpret_cast<const __nv_bfloat162*>(Vb + (size_t)ja * D + d0);
const float2 vf = __bfloat1622float2(vb);
acA[g][0] = fmaf(pk0, vf.x, acA[g][0]);
acA[g][1] = fmaf(pk0, vf.y, acA[g][1]);
}
if (jb < hi) {
const __nv_bfloat162 vb =
*reinterpret_cast<const __nv_bfloat162*>(Vb + (size_t)jb * D + d0);
const float2 vf = __bfloat1622float2(vb);
acB[g][0] = fmaf(pk1, vf.x, acB[g][0]);
acB[g][1] = fmaf(pk1, vf.y, acB[g][1]);
}
}
}
}
}
}
// ---- epilogue ----
const float li = 1.f / l_run;
#pragma unroll
for (int g = 0; g < GC; ++g) {
const int d0 = g * 64 + 2 * lane;
if (d0 < D) {
__nv_bfloat162 ob;
ob.x = __float2bfloat16((acA[g][0] + acB[g][0]) * li);
ob.y = __float2bfloat16((acA[g][1] + acB[g][1]) * li);
*reinterpret_cast<__nv_bfloat162*>(O + qoff + d0) = ob;
}
}
__syncwarp();
}
}
// ---------------------------------------------------------------------------
// Host entry
// ---------------------------------------------------------------------------
torch::Tensor nsa_forward(torch::Tensor q, torch::Tensor k, torch::Tensor v)
{
TORCH_CHECK(q.is_cuda() && k.is_cuda() && v.is_cuda(), "cuda tensors required");
TORCH_CHECK(q.scalar_type() == torch::kBFloat16, "q/k/v must be bf16");
auto qc = q.contiguous();
auto kc = k.contiguous();
auto vc = v.contiguous();
const int B = qc.size(0), H = qc.size(1), Sn = qc.size(2), Dn = qc.size(3);
TORCH_CHECK(kc.sizes() == qc.sizes() && vc.sizes() == qc.sizes(), "shape mismatch");
const int BH = B * H;
const int NB = (Sn + BSZ - 1) / BSZ;
auto O = torch::empty_like(qc);
auto opts = qc.options().dtype(torch::kFloat32);
auto P = torch::empty({(long)BH, (long)NB, (long)BSZ, (long)Dn}, opts);
cudaStream_t stream = at::cuda::getCurrentCUDAStream();
const float scale = 1.0f / std::sqrt((float)Dn);
{
dim3 grid(NB, BH);
int thr = ((Dn + 31) / 32) * 32;
nsa_prefix_kernel<<<grid, thr, 0, stream>>>(
reinterpret_cast<const __nv_bfloat16*>(kc.data_ptr()),
P.data_ptr<float>(), Sn, Dn, NB, BH);
}
const size_t smem = ((size_t)NWA * (Dn + NB + 1)) * sizeof(float);
TORCH_CHECK(smem <= 48 * 1024, "shared memory requirement too large");
dim3 grid((Sn + TPQ - 1) / TPQ, BH);
dim3 block(NWA * 32);
const __nv_bfloat16* qp = reinterpret_cast<const __nv_bfloat16*>(qc.data_ptr());
const __nv_bfloat16* kp = reinterpret_cast<const __nv_bfloat16*>(kc.data_ptr());
const __nv_bfloat16* vp = reinterpret_cast<const __nv_bfloat16*>(vc.data_ptr());
__nv_bfloat16* op = reinterpret_cast<__nv_bfloat16*>(O.data_ptr());
switch (Dn) {
case 64: nsa_attend_kernel<1><<<grid, block, smem, stream>>>(qp, kp, vp, P.data_ptr<float>(), op, Sn, Dn, NB, scale); break;
case 128: nsa_attend_kernel<2><<<grid, block, smem, stream>>>(qp, kp, vp, P.data_ptr<float>(), op, Sn, Dn, NB, scale); break;
default: TORCH_CHECK(false, "unsupported head dim (use 64 or 128)");
}
C10_CUDA_KERNEL_LAUNCH_CHECK();
return O;
}
"""
_CPP_SRC = "torch::Tensor nsa_forward(torch::Tensor q, torch::Tensor k, torch::Tensor v);"
_ext = None
def _get_ext():
global _ext
if _ext is None:
_ext = load_inline(
name="nsa_p6000_v7",
cpp_sources=[_CPP_SRC],
cuda_sources=[_CUDA_SRC],
functions=["nsa_forward"],
extra_cuda_cflags=["-O3", "--use_fast_math", "-lineinfo"],
verbose=False,
)
return _ext
def _nsa_fallback(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor) -> torch.Tensor:
"""Slow pure-torch path for head dims the CUDA kernels do not cover."""
qf, kf, vf = q.float(), k.float(), v.float()
Bh, Hn, Sn, Dn = qf.shape
scale = 1.0 / math.sqrt(Dn)
nb = (Sn + BLOCK_SIZE - 1) // BLOCK_SIZE
out = torch.zeros_like(qf)
for b in range(Bh):
for h in range(Hn):
sc_all = (qf[b, h] @ kf[b, h].transpose(0, 1)) * scale # (S, S)
for t in range(Sn):
scores = sc_all[t, : t + 1]
imps = []
for bi in range(nb):
s0 = bi * BLOCK_SIZE
if s0 > t:
continue
s1 = min(s0 + BLOCK_SIZE, t + 1)
imps.append((float(scores[s0:s1].mean()), bi))
imps.sort(reverse=True)
sel: set[int] = set()
for _, bi in imps[:TOP_N_BLOCKS]:
s0 = bi * BLOCK_SIZE
sel.update(range(s0, min(s0 + BLOCK_SIZE, t + 1)))
sel.update(range(max(0, t + 1 - SLIDING_WINDOW), t + 1))
idx = torch.tensor(sorted(sel), device=q.device, dtype=torch.long)
att = torch.softmax(scores.index_select(0, idx), dim=-1)
out[b, h, t] = att @ vf[b, h].index_select(0, idx)
return out
class Model(nn.Module):
def __init__(self, B: int, H: int, S: int, D: int):
super().__init__()
self.B, self.H, self.S, self.D = B, H, S, D
self.register_buffer("_dummy", torch.zeros(1, dtype=torch.bfloat16))
def forward(self, q: torch.Tensor, k: torch.Tensor, v: torch.Tensor) -> torch.Tensor:
d = q.size(-1)
s = q.size(-2)
if (
q.dtype != torch.bfloat16
or q.device.type != "cuda"
or d not in (64, 128)
or s > 16000 # shared-memory tile limit of the CUDA path
):
return _nsa_fallback(q, k, v).to(torch.bfloat16)
return _get_ext().nsa_forward(q, k, v)
def get_init_inputs():
return [B, H, S, D]
def get_inputs():
q = torch.randn(B, H, S, D, dtype=torch.bfloat16)
k = torch.randn(B, H, S, D, dtype=torch.bfloat16)
v = torch.randn(B, H, S, D, dtype=torch.bfloat16)
return [q, k, v]
20260822_102122_or-fable_stealth_ox-alpha_02_deepseek_nsa