kernelbench.com

KernelBench cuda · RTX PRO 6000

DeepSeek NSA Kimi K3 (1M)

5.84%geomean peak fraction across shapes

manually audited: clean

Genuine input-dependent NSA sparse attention in custom CUDA/PTX. Every call freshly mean-pools live K blocks, computes live Q/K causal block importance, selects the top eight with the reference tie-break, unions the exact local 64-token window, and applies sparse softmax attention to live V into a fresh output. There is no output cache, constant or fixed selection table, pointer-identity dispatch, CUDA graph, forbidden library, Triton/DSL path, cross-run artifact reuse, grader mutation, tolerance edit, or numeric-stress bypass. The six official latencies are 0.953664, 2.091424, 2.142048, 4.375360, 0.832736, and 1.491584 ms. NSA remains an ms/speedup-headline problem; the dense-equivalent 0.0584 scalar is arithmetically valid but is not a physically meaningful sparse-kernel roofline headline.

harnesskinetic-claudeagent session10h 39mtotal wall10h 49mcheck4mbenchmark6moutput tokens276,971cost$292.15gpu-lock wait7h 36mgpu-lock held22mregimecompute

Per-shape vs governing ceilingeach shape graded against whichever binds — bf16 compute or HBM bandwidth

1×16×2048×640.954 ms3.6%18 TFLOPS · 4% of 500 TF bf16 peak · also 0.02 TB/s (1% of HBM)
1×16×4127×642.091 ms6.7%33 TFLOPS · 7% of 500 TF bf16 peak · also 0.02 TB/s (1% of HBM)
1×8×8192×642.142 ms12.8%64 TFLOPS · 13% of 500 TF bf16 peak · also 0.02 TB/s (1% of HBM)
1×8×8191×1284.375 ms12.6%63 TFLOPS · 13% of 500 TF bf16 peak · also 0.02 TB/s (1% of HBM)
4×8×1024×640.833 ms2.1%10 TFLOPS · 2% of 500 TF bf16 peak · also 0.02 TB/s (1% of HBM)
2×8×3000×641.492 ms4.9%25 TFLOPS · 5% of 500 TF bf16 peak · also 0.02 TB/s (1% of HBM)

compute-bound memory-bound · bar + right column = official fraction of the ceiling (the geomean input)

geomean(3.6% · 6.7% · 12.8% · 12.6% · 2.1% · 4.9%) = 5.8%

Kernel source (redacted)
"""DeepSeek NSA-style block-select sparse attention - CUDA implementation (SM120).

Semantics (matches reference.nsa_attend): block importance = mean causal (q.k)/sqrt(D)
per 64-key block; top-8 blocks + 64-token sliding window; softmax attention over
the selected keys only. See check.py / problem.yaml.

Pipeline (3 kernels):
  K1 kbar_kernel:   block-mean keys Kbar (mean(q.k) == q.mean(k)), transposed
                    layout so scoring is a small coalesced GEMV, never dense QK^T.
  K2 select_kernel: batched top-8 selection for tiles of queries sharing one key
                    block; fp32 importances from smem-staged Kbar + exact causal
                    prefix dot for the current block. Writes per-query block lists.
  K3 attn_kernel:   per-query gather + two-pass softmax attention: cp.async
                    3-deep pipeline over 32-row segments, padded rows
                    (bank-conflict-free dots), bf16x2 vector V accumulation,
                    fp32 scores/softmax, bf16 output.

A vectorized PyTorch fallback covers shapes outside the CUDA fast path.
"""
from __future__ import annotations

import math

import torch
import torch.nn as nn
import torch.nn.functional as F

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 <cuda_runtime.h>

#define NT 256
#define NBMAX 128
#define TOPN 8
#define BSIZE 64
#define MAXKEYS 576
#define NSEGMAX 20
#define FULLMASK 0xffffffffu
#define NEGINF (-1e30f)

using bf16 = __nv_bfloat16;

// ---------------------------------------------------------------------------
// K1: block-mean keys, transposed layout  Kbar_T[(bh*D + d)*NB + bi]
// ---------------------------------------------------------------------------
template <int D>
__global__ void kbar_kernel(const bf16* __restrict__ K, float* __restrict__ KbarT,
                            int S, int nblocks) {
    const int bi = blockIdx.x;
    const int bh = blockIdx.y;
    const int rows = min(BSIZE, S - bi * BSIZE);
    const bf16* kb = K + ((long)bh * S + (long)bi * BSIZE) * D;
    __shared__ float part[128];
    constexpr int NSUB = 128 / D;
    const int d = threadIdx.x % D;
    const int sub = threadIdx.x / D;
    float acc = 0.f;
    for (int j = sub; j < rows; j += NSUB)
        acc += __bfloat162float(kb[(long)j * D + d]);
    if (NSUB > 1) {
        part[threadIdx.x] = acc;
        __syncthreads();
        if (sub == 0) {
            float tot = part[d];
            for (int s2 = 1; s2 < NSUB; s2++) tot += part[s2 * D + d];
            KbarT[((long)bh * D + d) * nblocks + bi] = tot / (float)rows;
        }
    } else {
        KbarT[((long)bh * D + d) * nblocks + bi] = acc / (float)rows;
    }
}

__device__ __forceinline__ void cp_async16(void* smem_dst, const void* gmem_src) {
    unsigned sdst = (unsigned)__cvta_generic_to_shared(smem_dst);
    asm volatile("cp.async.cg.shared.global [%0], [%1], 16;\n" ::"r"(sdst), "l"(gmem_src));
}
__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 stage_async(bf16* dst, const bf16* src, int nchunks16, int tid) {
    const char* g = (const char*)src;
    char* s = (char*)dst;
    for (int c = tid; c < nchunks16; c += NT) cp_async16(s + c * 16, g + c * 16);
    cp_commit();
}

// ---------------------------------------------------------------------------
// K2: batched top-8 selection.
// grid: (ceil(S/QT), BH). Tile of QT queries shares one key block cb.
// smem: [qbuf QT*D f32][kblk 64*D bf16][kbarc CHUNK*D f32][imp QT*NBMAX f32][pj QT*JG]
// ---------------------------------------------------------------------------
template <int D, int QT, int CHUNK>
__global__ void __launch_bounds__(NT)
select_kernel(const bf16* __restrict__ Q, const bf16* __restrict__ K,
              const float* __restrict__ KbarT, short* __restrict__ sel_out,
              int S, int nblocks, float scale) {
    extern __shared__ char smem_raw[];
    float* qbuf = (float*)smem_raw;                 // QT*D
    bf16* kblk = (bf16*)(qbuf + QT * D);            // BSIZE*D
    float* kbarc = (float*)(kblk + BSIZE * D);      // CHUNK*D
    float* imp = kbarc + CHUNK * D;                 // QT*NBMAX
    float* pj = imp + QT * NBMAX;                   // QT*8 scratch

    const int bh = blockIdx.y;
    const int t0 = blockIdx.x * QT;
    if (t0 >= S) return;
    const int tid = threadIdx.x;
    const int qrows = min(QT, S - t0);
    const int cb = t0 >> 6;
    const int nvalid = cb + 1;
    constexpr int JG = 8;                            // j-groups (NT/QT = 256/32)

    // stage Q tile -> fp32 smem
    {
        const bf16* qsrc = Q + ((long)bh * S + t0) * D;
        for (int i = tid; i < qrows * D; i += NT) qbuf[i] = __bfloat162float(qsrc[i]);
    }
    // stage current block K rows (clamped to S for a partial last block)
    const int kblk_rows = min(BSIZE, S - cb * BSIZE);
    stage_async(kblk, K + ((long)bh * S + cb * BSIZE) * D, kblk_rows * (D / 8), tid);
    // init imp to NEGINF while copies fly
    for (int i = tid; i < QT * NBMAX; i += NT) imp[i] = NEGINF;
    cp_wait<0>();
    __syncthreads();

    // ---- causal prefix importance for the shared current block ----
    {
        const int qi = tid >> 3;                   // 0..31
        const int jg = tid & 7;
        const int off = t0 - (cb << 6);            // 0 or 32
        float acc = 0.f;
        if (qi < qrows) {
            const int cnt = off + qi + 1;
            const float* q4 = qbuf + qi * D;
            for (int j = jg; j < cnt; j += JG) {
                const uint4* r4 = (const uint4*)(kblk + j * D);
                float dacc = 0.f;
#pragma unroll
                for (int dd = 0; dd < D / 8; dd++) {
                    uint4 raw = r4[dd];
                    const __nv_bfloat162* h2 = (const __nv_bfloat162*)&raw;
                    float2 f0 = __bfloat1622float2(h2[0]);
                    float2 f1 = __bfloat1622float2(h2[1]);
                    float2 f2 = __bfloat1622float2(h2[2]);
                    float2 f3 = __bfloat1622float2(h2[3]);
                    const float* qd = q4 + dd * 8;
                    dacc = fmaf(f0.x, qd[0], dacc); dacc = fmaf(f0.y, qd[1], dacc);
                    dacc = fmaf(f1.x, qd[2], dacc); dacc = fmaf(f1.y, qd[3], dacc);
                    dacc = fmaf(f2.x, qd[4], dacc); dacc = fmaf(f2.y, qd[5], dacc);
                    dacc = fmaf(f3.x, qd[6], dacc); dacc = fmaf(f3.y, qd[7], dacc);
                }
                acc += dacc;
            }
        }
        pj[tid] = acc;
        __syncthreads();
        if (tid < qrows) {
            float tot = 0.f;
#pragma unroll
            for (int g2 = 0; g2 < JG; g2++) tot += pj[tid * JG + g2];
            const int cnt = off + tid + 1;
            imp[tid * NBMAX + cb] = tot * scale / (float)cnt;
        }
        __syncthreads();
    }

    // ---- Kbar importances in chunks ----
    for (int c0 = 0; c0 < cb; c0 += CHUNK) {
        const int cn = min(CHUNK, cb - c0);
        for (int i = tid; i < D * cn; i += NT) {
            const int dd = i / cn;
            const int j = i - dd * cn;
            kbarc[dd * CHUNK + j] = KbarT[((long)bh * D + dd) * nblocks + c0 + j];
        }
        __syncthreads();
        const int qi = tid >> 3;
        const int bg = tid & 7;
        if (qi < qrows) {
            const float* q4 = qbuf + qi * D;
#pragma unroll 2
            for (int j = bg; j < cn; j += 8) {
                float a0 = 0.f, a1 = 0.f, a2 = 0.f, a3 = 0.f;
                for (int dd = 0; dd < D; dd += 4) {
                    a0 = fmaf(kbarc[(dd + 0) * CHUNK + j], q4[dd + 0], a0);
                    a1 = fmaf(kbarc[(dd + 1) * CHUNK + j], q4[dd + 1], a1);
                    a2 = fmaf(kbarc[(dd + 2) * CHUNK + j], q4[dd + 2], a2);
                    a3 = fmaf(kbarc[(dd + 3) * CHUNK + j], q4[dd + 3], a3);
                }
                imp[qi * NBMAX + c0 + j] = ((a0 + a1) + (a2 + a3)) * scale;
            }
        }
        __syncthreads();
    }

    // ---- top-8 per query (one warp per query), pad tail with -1 ----
    {
        const int warp = tid >> 5, lane = tid & 31;
        for (int qi = warp; qi < qrows; qi += NT / 32) {
            float* row = imp + qi * NBMAX;
            float bv = NEGINF;
            int bb = -1;
            int nsel = 0;
#pragma unroll
            for (int r = 0; r < TOPN; r++) {
                bv = NEGINF; bb = -1;
                for (int bi = lane; bi < nvalid; bi += 32) {
                    float v = row[bi];
                    if (v > NEGINF && (v > bv || (v == bv && bi > bb))) { bv = v; bb = bi; }
                }
                for (int off2 = 16; off2; off2 >>= 1) {
                    float ov = __shfl_xor_sync(FULLMASK, bv, off2);
                    int ob = __shfl_xor_sync(FULLMASK, bb, off2);
                    if (ov > NEGINF && (ov > bv || (ov == bv && ob > bb))) { bv = ov; bb = ob; }
                }
                if (bb < 0) break;
                if (lane == 0) {
                    sel_out[((long)bh * S + t0 + qi) * TOPN + r] = (short)bb;
                    nsel = r + 1;
                }
                for (int bi = lane; bi < nvalid; bi += 32)
                    if (bi == bb) row[bi] = NEGINF;
                __syncwarp();
            }
            if (lane == 0) {
                short* so = sel_out + ((long)bh * S + t0 + qi) * TOPN;
                for (int r = nsel; r < TOPN; r++) so[r] = -1;
            }
        }
    }
}

// ---------------------------------------------------------------------------
// K3: per-query gather + two-pass softmax attention (v1 attention, no select)
// ANT threads per CTA; scores use TPK = ANT/64 threads per key.
// Staging rows are padded (stride D+PAD bf16) so consecutive rows rotate
// across smem banks (conflict-free dots and V reads).
// ---------------------------------------------------------------------------
#define PADROW 8
#define RWORDS ((D + PADROW) / 8)                       // uint4 per padded row

template <int D, int TPK>
__device__ __forceinline__ float dot_row_smem(const bf16* stage, int j, const float4* q4, int g) {
    constexpr int PT = D / (8 * TPK);                   // uint4 chunks per thread
    constexpr int RW = (D + PADROW) / 8;
    const uint4* s4 = (const uint4*)stage;
    float acc0 = 0.f, acc1 = 0.f;
#pragma unroll
    for (int c = 0; c < PT; c += 2) {
#pragma unroll
        for (int cc = 0; cc < 2; cc++) {
            if (c + cc < PT) {
                uint4 raw = s4[j * RW + g * PT + c + cc];
                float4 qa = q4[(g * PT + c + cc) * 2];
                float4 qb = q4[(g * PT + c + cc) * 2 + 1];
                const __nv_bfloat162* h2 = (const __nv_bfloat162*)&raw;
                float2 f0 = __bfloat1622float2(h2[0]);
                float2 f1 = __bfloat1622float2(h2[1]);
                float2 f2 = __bfloat1622float2(h2[2]);
                float2 f3 = __bfloat1622float2(h2[3]);
                if (cc == 0) {
                    acc0 = fmaf(f0.x, qa.x, acc0); acc0 = fmaf(f0.y, qa.y, acc0);
                    acc0 = fmaf(f1.x, qa.z, acc0); acc0 = fmaf(f1.y, qa.w, acc0);
                    acc0 = fmaf(f2.x, qb.x, acc0); acc0 = fmaf(f2.y, qb.y, acc0);
                    acc0 = fmaf(f3.x, qb.z, acc0); acc0 = fmaf(f3.y, qb.w, acc0);
                } else {
                    acc1 = fmaf(f0.x, qa.x, acc1); acc1 = fmaf(f0.y, qa.y, acc1);
                    acc1 = fmaf(f1.x, qa.z, acc1); acc1 = fmaf(f1.y, qa.w, acc1);
                    acc1 = fmaf(f2.x, qb.x, acc1); acc1 = fmaf(f2.y, qb.y, acc1);
                    acc1 = fmaf(f3.x, qb.z, acc1); acc1 = fmaf(f3.y, qb.w, acc1);
                }
            }
        }
    }
    return acc0 + acc1;
}

template <int ANT>
__device__ __forceinline__ float blk_max(float v, float* red, int tid) {
    constexpr int WARPS = ANT / 32;
    for (int off = 16; off; off >>= 1) v = fmaxf(v, __shfl_down_sync(FULLMASK, v, off));
    if ((tid & 31) == 0) red[tid >> 5] = v;
    __syncthreads();
    if (tid < 32) {
        v = (tid < WARPS) ? red[tid] : NEGINF;
        for (int off = WARPS >> 1; off; off >>= 1) v = fmaxf(v, __shfl_down_sync(FULLMASK, v, off));
        if (tid == 0) red[0] = v;
    }
    __syncthreads();
    return red[0];
}
template <int ANT>
__device__ __forceinline__ float blk_sum(float v, float* red, int tid) {
    constexpr int WARPS = ANT / 32;
    for (int off = 16; off; off >>= 1) v += __shfl_down_sync(FULLMASK, v, off);
    if ((tid & 31) == 0) red[tid >> 5] = v;
    __syncthreads();
    if (tid < 32) {
        v = (tid < WARPS) ? red[tid] : 0.f;
        for (int off = WARPS >> 1; off; off >>= 1) v += __shfl_down_sync(FULLMASK, v, off);
        if (tid == 0) red[0] = v;
    }
    __syncthreads();
    return red[0];
}

// stage rows of a (S,D) bf16 matrix into a padded-stride smem buffer
template <int D>
__device__ __forceinline__ void stage_async_n(bf16* dst, const bf16* src, int len, int tid, int nt) {
    constexpr int RW = (D + PADROW) / 8;                // uint4 per padded row
    const int nchunks = len * (D / 8);
    const char* g = (const char*)src;
    char* s = (char*)dst;
    for (int c = tid; c < nchunks; c += nt) {
        const int row = c / (D / 8);
        const int off16 = c - row * (D / 8);
        cp_async16(s + (row * RW + off16) * 16, g + c * 16);
    }
    cp_commit();
}

template <int D, int ANT, int SEGCAP, int PD>
__global__ void __launch_bounds__(ANT)
attn_kernel(const bf16* __restrict__ Q, const bf16* __restrict__ K,
            const bf16* __restrict__ V, const short* __restrict__ sel_in,
            bf16* __restrict__ O, int B, int H, int S, float scale) {
    constexpr int TPK = ANT / 64;                       // threads per key in phase 1
    constexpr int RSTRIDE = D + PADROW;                 // padded row stride (bf16)
    constexpr int NPAIR = D / 2;                        // bf16x2 pairs per row
    constexpr int KGV = ANT / NPAIR;                    // key groups in phase 2
    constexpr int NBUF = PD + 1;                        // stage buffers
    extern __shared__ char smem_raw[];
    bf16* stage = (bf16*)smem_raw;                      // NBUF * SEGCAP * RSTRIDE bf16
    float* q_s = (float*)(smem_raw + (long)NBUF * SEGCAP * RSTRIDE * 2); // D floats
    float* scores_s = q_s + D;                          // 576 floats
    float* opart = scores_s + MAXKEYS;                  // KGV*D floats
    float* red_s = opart + KGV * D;                     // 32 floats
    int* seg_lo = (int*)(red_s + 32);                   // 10
    int* seg_len = seg_lo + NSEGMAX;                    // 10
    int* seg_off = seg_len + NSEGMAX;                   // 10
    int* cnt_s = seg_off + NSEGMAX;                     // nseg, nkeys

    const int xid = blockIdx.x;
    const int t = xid % S;
    const int bh = xid / S;
    const int tid = threadIdx.x;
    const int w0 = max(0, t + 1 - BSIZE);
    const bf16* Kbh = K + (long)bh * S * D;
    const bf16* Vbh = V + (long)bh * S * D;

    if (tid < D) q_s[tid] = __bfloat162float(Q[((long)bh * S + t) * D + tid]);
    // ---- load selection + build segments (warp 0 lane 0) ----
    __syncthreads();
    if (tid == 0) {
        int off = 0, nseg = 0;
        const short* si = sel_in + (long)xid * TOPN;
        for (int r = 0; r < TOPN; r++) {
            int bi = si[r];
            if (bi < 0) break;
            int lo = bi * BSIZE;
            int hi = min(min(lo + BSIZE, t + 1), w0);
            for (int b0 = lo; b0 < hi; b0 += SEGCAP) {
                int c = min(hi - b0, SEGCAP);
                seg_lo[nseg] = b0; seg_len[nseg] = c; seg_off[nseg] = off;
                off += c; nseg++;
            }
        }
        int a = w0, b = t + 1;
        for (; a < b; a += SEGCAP) {
            int c = min(b - a, SEGCAP);
            seg_lo[nseg] = a; seg_len[nseg] = c; seg_off[nseg] = off;
            off += c; nseg++;
        }
        cnt_s[0] = nseg;
        cnt_s[1] = off;
    }
    __syncthreads();
    const int nseg = cnt_s[0];
    const int nkeys = cnt_s[1];
    const float4* q4 = (const float4*)q_s;

    // ---- phase 1: stage K segments, compute scores ----
#pragma unroll
    for (int p = 0; p < PD; p++)
        if (p < nseg) stage_async_n<D>(stage + p * SEGCAP * RSTRIDE,
                                       Kbh + (long)seg_lo[p] * D, seg_len[p], tid, ANT);
    for (int s = 0; s < nseg; s++) {
        bf16* buf = stage + (s % NBUF) * SEGCAP * RSTRIDE;
        if (s + PD < nseg) {
            stage_async_n<D>(stage + ((s + PD) % NBUF) * SEGCAP * RSTRIDE,
                             Kbh + (long)seg_lo[s + PD] * D, seg_len[s + PD], tid, ANT);
            cp_wait<PD>();
        } else if (s + 2 == nseg) {
            cp_wait<1>();
        } else {
            cp_wait<0>();
        }
        __syncthreads();
        const int len = seg_len[s];
        const int j = tid / TPK, g = tid % TPK;
        float acc = 0.f;
        if (j < len) acc = dot_row_smem<D, TPK>(buf, j, q4, g);
        for (int off = 1; off < TPK; off <<= 1) acc += __shfl_xor_sync(FULLMASK, acc, off);
        if (g == 0 && j < len) scores_s[seg_off[s] + j] = acc * scale;
        __syncthreads();
    }

    // ---- softmax normalize ----
    {
        float m = NEGINF;
        for (int i = tid; i < nkeys; i += ANT) m = fmaxf(m, scores_s[i]);
        m = blk_max<ANT>(m, red_s, tid);
        float l = 0.f;
        for (int i = tid; i < nkeys; i += ANT) {
            float p = __expf(scores_s[i] - m);
            scores_s[i] = p;
            l += p;
        }
        blk_sum<ANT>(l, red_s, tid);
    }

    // ---- phase 2: stage V segments, accumulate (bf16x2 vector reads) ----
#pragma unroll
    for (int p = 0; p < PD; p++)
        if (p < nseg) stage_async_n<D>(stage + p * SEGCAP * RSTRIDE,
                                       Vbh + (long)seg_lo[p] * D, seg_len[p], tid, ANT);
    const int dp = tid % NPAIR;                         // dim pair owned
    const int kg = tid / NPAIR;                         // key group
    float oa0 = 0.f, oa1 = 0.f;
    const float rsum = red_s[0];
    for (int s = 0; s < nseg; s++) {
        const bf16* buf = stage + (s % NBUF) * SEGCAP * RSTRIDE;
        if (s + PD < nseg) {
            stage_async_n<D>(stage + ((s + PD) % NBUF) * SEGCAP * RSTRIDE,
                             Vbh + (long)seg_lo[s + PD] * D, seg_len[s + PD], tid, ANT);
            cp_wait<PD>();
        } else if (s + 2 == nseg) {
            cp_wait<1>();
        } else {
            cp_wait<0>();
        }
        __syncthreads();
        const int len = seg_len[s];
        const int soff = seg_off[s];
        const float* p = scores_s + soff;
#pragma unroll 2
        for (int j = kg; j < len; j += KGV) {
            const float pj = p[j];
            const float2 vv = __bfloat1622float2(*(const __nv_bfloat162*)&buf[j * RSTRIDE + dp * 2]);
            oa0 = fmaf(pj, vv.x, oa0);
            oa1 = fmaf(pj, vv.y, oa1);
        }
        __syncthreads();
    }
    opart[kg * D + dp * 2] = oa0;
    opart[kg * D + dp * 2 + 1] = oa1;
    __syncthreads();
    if (tid < D) {
        float acc = opart[tid];
        for (int s2 = 1; s2 < KGV; s2++) acc += opart[s2 * D + tid];
        O[((long)bh * S + t) * D + tid] = __float2bfloat16(acc / rsum);
    }
}
// ---------------------------------------------------------------------------
// host
// ---------------------------------------------------------------------------
torch::Tensor nsa_forward_v2(torch::Tensor Q, torch::Tensor K, torch::Tensor V, long variant) {
    const int Bn = Q.size(0), Hn = Q.size(1), Sn = Q.size(2), Dn = Q.size(3);
    const int BH = Bn * Hn;
    const int nblocks = (Sn + BSIZE - 1) / BSIZE;
    TORCH_CHECK(nblocks <= NBMAX, "S too large for fast path");
    auto O = torch::empty_like(Q);
    auto Kbar = torch::empty({(long)BH * Dn * nblocks},
                             torch::TensorOptions().dtype(torch::kFloat32).device(Q.device()));
    auto sel = torch::empty({(long)BH * Sn * TOPN},
                            torch::TensorOptions().dtype(torch::kInt16).device(Q.device()));
    cudaStream_t stream = at::cuda::getCurrentCUDAStream();
    const float scale = (float)(1.0 / std::sqrt((double)Dn));

    dim3 g1(nblocks, BH);
    dim3 g3(BH * Sn);
    const int PAD = PADROW;
    if (Dn == 64) {
        kbar_kernel<64><<<g1, 128, 0, stream>>>(
            (const bf16*)K.data_ptr(), Kbar.data_ptr<float>(), Sn, nblocks);
        constexpr int QT = 32, CHUNK = 32;
        dim3 gs((Sn + QT - 1) / QT, BH);
        const int smem_s = QT * 64 * 4 + BSIZE * 64 * 2 + CHUNK * 64 * 4 + QT * NBMAX * 4 + NT * 4;
        select_kernel<64, QT, CHUNK><<<gs, NT, smem_s, stream>>>(
            (const bf16*)Q.data_ptr(), (const bf16*)K.data_ptr(), Kbar.data_ptr<float>(),
            (short*)sel.data_ptr(), Sn, nblocks, scale);
        const int RSTR = 64 + PAD;
        switch (variant) {
            case 1: {  // 256 thr, 32-row segs, 3-buf
                const int smem = 3 * 32 * RSTR * 2 + (64 + MAXKEYS + 8 * 64 + 32) * 4 + 76 * 4;
                attn_kernel<64, 256, 32, 2><<<g3, 256, smem, stream>>>(
                    (const bf16*)Q.data_ptr(), (const bf16*)K.data_ptr(), (const bf16*)V.data_ptr(),
                    (const short*)sel.data_ptr(), (bf16*)O.data_ptr(), Bn, Hn, Sn, scale);
                break; }
            case 2: {  // 512 thr, 64-row segs, 2-buf
                const int smem = 2 * BSIZE * RSTR * 2 + (64 + MAXKEYS + 16 * 64 + 32) * 4 + 76 * 4;
                attn_kernel<64, 512, 64, 1><<<g3, 512, smem, stream>>>(
                    (const bf16*)Q.data_ptr(), (const bf16*)K.data_ptr(), (const bf16*)V.data_ptr(),
                    (const short*)sel.data_ptr(), (bf16*)O.data_ptr(), Bn, Hn, Sn, scale);
                break; }
            case 3: {  // 512 thr, 32-row segs, 3-buf
                const int smem = 3 * 32 * RSTR * 2 + (64 + MAXKEYS + 16 * 64 + 32) * 4 + 76 * 4;
                attn_kernel<64, 512, 32, 2><<<g3, 512, smem, stream>>>(
                    (const bf16*)Q.data_ptr(), (const bf16*)K.data_ptr(), (const bf16*)V.data_ptr(),
                    (const short*)sel.data_ptr(), (bf16*)O.data_ptr(), Bn, Hn, Sn, scale);
                break; }
            default: { // 256 thr, 64-row segs, 2-buf (baseline)
                const int smem = 2 * BSIZE * RSTR * 2 + (64 + MAXKEYS + 8 * 64 + 32) * 4 + 76 * 4;
                attn_kernel<64, 256, 64, 1><<<g3, 256, smem, stream>>>(
                    (const bf16*)Q.data_ptr(), (const bf16*)K.data_ptr(), (const bf16*)V.data_ptr(),
                    (const short*)sel.data_ptr(), (bf16*)O.data_ptr(), Bn, Hn, Sn, scale);
                break; }
        }
    } else if (Dn == 128) {
        kbar_kernel<128><<<g1, 128, 0, stream>>>(
            (const bf16*)K.data_ptr(), Kbar.data_ptr<float>(), Sn, nblocks);
        constexpr int QT = 16, CHUNK = 16;
        dim3 gs((Sn + QT - 1) / QT, BH);
        const int smem_s = QT * 128 * 4 + BSIZE * 128 * 2 + CHUNK * 128 * 4 + QT * NBMAX * 4 + NT * 4;
        select_kernel<128, QT, CHUNK><<<gs, NT, smem_s, stream>>>(
            (const bf16*)Q.data_ptr(), (const bf16*)K.data_ptr(), Kbar.data_ptr<float>(),
            (short*)sel.data_ptr(), Sn, nblocks, scale);
        const int RSTR = 128 + PAD;
        switch (variant) {
            case 1: {  // 256 thr, 32-row segs, 3-buf
                const int smem = 3 * 32 * RSTR * 2 + (128 + MAXKEYS + 4 * 128 + 32) * 4 + 76 * 4;
                attn_kernel<128, 256, 32, 2><<<g3, 256, smem, stream>>>(
                    (const bf16*)Q.data_ptr(), (const bf16*)K.data_ptr(), (const bf16*)V.data_ptr(),
                    (const short*)sel.data_ptr(), (bf16*)O.data_ptr(), Bn, Hn, Sn, scale);
                break; }
            case 2: {  // 512 thr, 64-row segs, 2-buf (baseline d128)
                const int smem = 2 * BSIZE * RSTR * 2 + (128 + MAXKEYS + 8 * 128 + 32) * 4 + 76 * 4;
                attn_kernel<128, 512, 64, 1><<<g3, 512, smem, stream>>>(
                    (const bf16*)Q.data_ptr(), (const bf16*)K.data_ptr(), (const bf16*)V.data_ptr(),
                    (const short*)sel.data_ptr(), (bf16*)O.data_ptr(), Bn, Hn, Sn, scale);
                break; }
            case 3: {  // 512 thr, 32-row segs, 3-buf
                const int smem = 3 * 32 * RSTR * 2 + (128 + MAXKEYS + 8 * 128 + 32) * 4 + 76 * 4;
                attn_kernel<128, 512, 32, 2><<<g3, 512, smem, stream>>>(
                    (const bf16*)Q.data_ptr(), (const bf16*)K.data_ptr(), (const bf16*)V.data_ptr(),
                    (const short*)sel.data_ptr(), (bf16*)O.data_ptr(), Bn, Hn, Sn, scale);
                break; }
            default: { // 256 thr, 64-row segs, 2-buf
                const int smem = 2 * BSIZE * RSTR * 2 + (128 + MAXKEYS + 4 * 128 + 32) * 4 + 76 * 4;
                attn_kernel<128, 256, 64, 1><<<g3, 256, smem, stream>>>(
                    (const bf16*)Q.data_ptr(), (const bf16*)K.data_ptr(), (const bf16*)V.data_ptr(),
                    (const short*)sel.data_ptr(), (bf16*)O.data_ptr(), Bn, Hn, Sn, scale);
                break; }
        }
    } else {
        TORCH_CHECK(false, "unsupported head dim");
    }
    return O;
}
"""



CPP_SRC = "torch::Tensor nsa_forward_v2(torch::Tensor Q, torch::Tensor K, torch::Tensor V, long variant);"

_ext = None


def _build():
    global _ext
    if _ext is not None:
        return _ext
    from torch.utils.cpp_extension import load_inline

    _ext = load_inline(
        name="nsa_sparse_attn_v4",
        cpp_sources=[CPP_SRC],
        cuda_sources=[CUDA_SRC],
        functions=["nsa_forward_v2"],
        verbose=False,
        extra_cflags=["-O3", "-std=c++17"],
        extra_cuda_cflags=[
            "-O3",
            "-std=c++17",
            "--use_fast_math",
            "-gencode=arch=compute_120a,code=sm_120a",
        ],
    )
    return _ext


def _nsa_torch_fallback(q, k, v, bs=64, topn=8, win=64):
    """Vectorized PyTorch port of the reference semantics.

    Correctness safety net for shapes outside the CUDA fast path
    (D not in {64,128} or S > 8192). Not used by the public deck.
    """
    qf, kf, vf = q.float(), k.float(), v.float()
    Bb, Hh, Ss, Dd = qf.shape
    scale = 1.0 / math.sqrt(Dd)
    nblocks = (Ss + bs - 1) // bs
    device = qf.device
    out = torch.empty_like(qf)
    ar = torch.arange(Ss, device=device)
    for b in range(Bb):
        for h in range(Hh):
            sc = (qf[b, h] @ kf[b, h].transpose(0, 1)) * scale  # (S,S) fp32
            imp = torch.full((Ss, nblocks), -1e9, device=device)
            for bi in range(nblocks):
                s0 = bi * bs
                s1 = min(s0 + bs, Ss)
                col = sc[:, s0:s1]  # (S, len)
                cc = col.cumsum(1)
                # rows t >= s0: causal prefix over columns [s0, min(t, s1-1)]
                t_lo = s0
                tvec_r = ar[t_lo:]
                idx = torch.clamp(tvec_r - s0, max=s1 - s0 - 1)
                rowsum = cc[t_lo:].gather(1, idx.unsqueeze(1)).squeeze(1)
                cnt = (idx + 1).float()
                imp[t_lo:, bi] = rowsum / cnt
            k_ = min(topn, nblocks)
            topv, topi = torch.topk(imp, k=k_, dim=1)
            # selected mask over keys, causal, union sliding window
            keymask = qf.new_zeros((Ss, Ss), dtype=torch.bool)
            tvec = ar.unsqueeze(1)
            for r in range(k_):
                bi_r = topi[:, r : r + 1]  # (S,1)
                lo = bi_r * bs
                valid = (topv[:, r : r + 1] > -1e8) & (lo <= tvec)
                inblk = (ar.unsqueeze(0) >= lo) & (ar.unsqueeze(0) < lo + bs) & (ar.unsqueeze(0) <= tvec)
                keymask |= inblk & valid
            w0 = torch.clamp(tvec - win + 1, min=0)
            keymask |= (ar.unsqueeze(0) >= w0) & (ar.unsqueeze(0) <= tvec)
            sc_masked = sc.masked_fill(~keymask, float("-inf"))
            att = torch.softmax(sc_masked, dim=-1)
            out[b, h] = att @ vf[b, h]
    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))
        self._ext = None
        if D in (64, 128) and S <= 8192:
            try:
                self._ext = _build()
            except Exception:
                self._ext = None

    def forward(self, q: torch.Tensor, k: torch.Tensor, v: torch.Tensor) -> torch.Tensor:
        if (
            self._ext is not None
            and q.is_cuda
            and q.dtype == torch.bfloat16
            and q.dim() == 4
            and q.size(-1) in (64, 128)
            and q.size(2) <= 8192
        ):
            # variant 1: 256 threads, 32-row segments, 3-deep cp.async pipeline
            return self._ext.nsa_forward_v2(q.contiguous(), k.contiguous(), v.contiguous(), 1)
        return _nsa_torch_fallback(q, k, v, BLOCK_SIZE, TOP_N_BLOCKS, SLIDING_WINDOW).to(torch.bfloat16)


# Eagerly build the extension at import so compile cost is outside the timed path.
if torch.cuda.is_available():
    try:
        _build()
    except Exception:
        pass

20260716_150116_kinetic-claude_kinetic-0715_1m__02_deepseek_nsa