kernelbench.com

KernelBench hard · H100

TopK Bitonic Claude Opus 4.8

builddid not score
harnessclaudeagent session45mtotal wall47mcheck2mbenchmarkoutput tokens1,103gpu-lock wait89sgpu-lock held4sregimememory

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

No per-shape benchmark data archived for this run.

Kernel source (redacted)
import torch
import torch.nn as nn
from torch.utils.cpp_extension import load_inline

_CUDA_SRC = r"""
#include <torch/extension.h>
#include <cuda.h>
#include <cuda_runtime.h>
#include <c10/cuda/CUDAStream.h>
#include <math_constants.h>

#define KMAX 64

__device__ __forceinline__ unsigned int fkey(float f) {
    unsigned int b = __float_as_uint(f);
    return b ^ ((b >> 31) ? 0xFFFFFFFFu : 0x80000000u);
}

// Block-level top-k of (sv,si)[0..len). Writes k sorted-descending results to out_val/out_idx.
__device__ void block_topk(float* sv, int* si, int len, int k,
                           float* out_val, long* out_idx) {
    __shared__ unsigned int hist[256];
    __shared__ int sc[256];
    __shared__ unsigned int s_prefix;
    __shared__ int s_need, s_base, s_cgt, s_ceq;
    __shared__ float topv[KMAX];
    __shared__ int   topi[KMAX];

    int T = blockDim.x, tid = threadIdx.x;
    if (tid == 0) { s_prefix = 0; s_need = k; }
    __syncthreads();

    for (int pass = 0; pass < 4; pass++) {
        int shift = 24 - 8 * pass;
        unsigned int curmask = (shift + 8 >= 32) ? 0u : (0xFFFFFFFFu << (shift + 8));
        for (int b = tid; b < 256; b += T) hist[b] = 0;
        __syncthreads();
        unsigned int prefix = s_prefix;
        for (int i = tid; i < len; i += T) {
            unsigned int u = fkey(sv[i]);
            if ((u & curmask) == prefix) {
                unsigned int d = (u >> shift) & 0xFFu;
                unsigned int active = __activemask();
                unsigned int same = __match_any_sync(active, d);
                int leader = __ffs(same) - 1;
                int cnt = __popc(same);
                if ((threadIdx.x & 31) == leader) atomicAdd(&hist[d], (unsigned)cnt);
            }
        }
        // suffix-sum of hist into sc[d] = sum_{j>=d} hist[j], in parallel
        if (tid < 256) sc[tid] = (int)hist[tid];
        __syncthreads();
        for (int off = 1; off < 256; off <<= 1) {
            int add = 0;
            if (tid < 256 && tid + off < 256) add = sc[tid + off];
            __syncthreads();
            if (tid < 256) sc[tid] += add;
            __syncthreads();
        }
        int need = s_need;
        if (tid < 256 && sc[tid] >= need && (tid == 255 || sc[tid + 1] < need)) {
            s_prefix = prefix | ((unsigned int)tid << shift);
            s_need = need - (tid == 255 ? 0 : sc[tid + 1]);
        }
        __syncthreads();
    }

    if (tid == 0) { s_base = k - s_need; s_cgt = 0; s_ceq = 0; }
    __syncthreads();
    unsigned int u_thr = s_prefix;
    int base = s_base, need = s_need;
    for (int i = tid; i < len; i += T) {
        unsigned int u = fkey(sv[i]);
        if (u > u_thr) {
            int p = atomicAdd(&s_cgt, 1);
            if (p < base) { topv[p] = sv[i]; topi[p] = si[i]; }
        } else if (u == u_thr) {
            int p = atomicAdd(&s_ceq, 1);
            if (p < need) { topv[base + p] = sv[i]; topi[base + p] = si[i]; }
        }
    }
    __syncthreads();

    int KP = 1; while (KP < k) KP <<= 1;
    for (int i = tid + k; i < KP; i += T) { topv[i] = -CUDART_INF_F; topi[i] = 0; }
    __syncthreads();
    for (int kk = 2; kk <= KP; kk <<= 1)
        for (int j = kk >> 1; j > 0; j >>= 1) {
            for (int i = tid; i < KP; i += T) {
                int ixj = i ^ j;
                if (ixj > i) {
                    bool up = ((i & kk) == 0);
                    float a = topv[i], b = topv[ixj];
                    if ((a > b) == up) {
                        topv[i] = b; topv[ixj] = a;
                        int t = topi[i]; topi[i] = topi[ixj]; topi[ixj] = t;
                    }
                }
            }
            __syncthreads();
        }
    for (int j = tid; j < k; j += T) {
        out_val[j] = topv[KP - 1 - j];
        out_idx[j] = topi[KP - 1 - j];
    }
}

__global__ void selectk_kernel(const float* __restrict__ x, float* __restrict__ ov,
                               long* __restrict__ oi, int n, int k, int chunk, int blocks_per_row) {
    extern __shared__ char smem[];
    float* sv = (float*)smem;
    int* si = (int*)(sv + chunk);
    int row = blockIdx.x / blocks_per_row;
    int cid = blockIdx.x % blocks_per_row;
    int cstart = cid * chunk;
    int len = chunk;
    if (cstart + len > n) len = n - cstart;
    const float* xr = x + (long)row * n + cstart;
    for (int i = threadIdx.x; i < len; i += blockDim.x) { sv[i] = xr[i]; si[i] = cstart + i; }
    __syncthreads();
    long outbase = ((long)row * blocks_per_row + cid) * k;
    block_topk(sv, si, len, k, ov + outbase, oi + outbase);
}

__global__ void merge_kernel(const float* __restrict__ cv, const long* __restrict__ ci,
                             float* __restrict__ ov, long* __restrict__ oi, int m, int k) {
    extern __shared__ char smem[];
    float* sv = (float*)smem;
    int* si = (int*)(sv + m);
    int row = blockIdx.x;
    const float* c = cv + (long)row * m;
    const long* d = ci + (long)row * m;
    for (int i = threadIdx.x; i < m; i += blockDim.x) { sv[i] = c[i]; si[i] = (int)d[i]; }
    __syncthreads();
    block_topk(sv, si, m, k, ov + (long)row * k, oi + (long)row * k);
}

static int next_pow2(int x) { int p = 1; while (p < x) p <<= 1; return p; }

static void set_smem(const void* fn, int bytes) {
    if (bytes > 48 * 1024)
        cudaFuncSetAttribute((void*)fn, cudaFuncAttributeMaxDynamicSharedMemorySize, bytes);
}

std::vector<torch::Tensor> topk_forward(torch::Tensor x, int64_t k) {
    int batch = x.size(0);
    int n = x.size(1);
    auto stream = at::cuda::getCurrentCUDAStream();
    auto opt_v = torch::TensorOptions().dtype(torch::kFloat32).device(x.device());
    auto opt_i = torch::TensorOptions().dtype(torch::kInt64).device(x.device());
    torch::Tensor out_val = torch::empty({batch, (long)k}, opt_v);
    torch::Tensor out_idx = torch::empty({batch, (long)k}, opt_i);

    const int SHARED_CAP_ELEMS = 24 * 1024;
    int chunk, blocks_per_row;
    if (n <= SHARED_CAP_ELEMS) { chunk = n; blocks_per_row = 1; }
    else { chunk = 2048; blocks_per_row = (n + chunk - 1) / chunk; }

    int T = 256;
    int smem = chunk * (sizeof(float) + sizeof(int));

    if (blocks_per_row == 1) {
        set_smem((const void*)selectk_kernel, smem);
        selectk_kernel<<<batch, T, smem, stream>>>(
            x.data_ptr<float>(), out_val.data_ptr<float>(), out_idx.data_ptr<long>(),
            n, k, chunk, 1);
    } else {
        torch::Tensor cv = torch::empty({batch, (long)blocks_per_row * (long)k}, opt_v);
        torch::Tensor ci = torch::empty({batch, (long)blocks_per_row * (long)k}, opt_i);
        set_smem((const void*)selectk_kernel, smem);
        selectk_kernel<<<batch * blocks_per_row, T, smem, stream>>>(
            x.data_ptr<float>(), cv.data_ptr<float>(), ci.data_ptr<long>(),
            n, k, chunk, blocks_per_row);
        int m = blocks_per_row * k;
        int Tm = 256;
        int smem_m = m * (sizeof(float) + sizeof(int));
        set_smem((const void*)merge_kernel, smem_m);
        merge_kernel<<<batch, Tm, smem_m, stream>>>(
            cv.data_ptr<float>(), ci.data_ptr<long>(),
            out_val.data_ptr<float>(), out_idx.data_ptr<long>(), m, k);
    }
    return {out_val, out_idx};
}
"""

_CPP_SRC = "std::vector<torch::Tensor> topk_forward(torch::Tensor x, int64_t k);"

_mod = load_inline(
    name="topk_radix_v2",
    cpp_sources=_CPP_SRC,
    cuda_sources=_CUDA_SRC,
    functions=["topk_forward"],
    extra_cuda_cflags=["-O3", "--use_fast_math"],
    verbose=False,
)


class Model(nn.Module):
    def __init__(self, batch: int, n: int, k: int):
        super().__init__()
        self.batch, self.n, self.k = batch, n, k
        self.register_buffer("_dummy", torch.zeros(1))

    def forward(self, x: torch.Tensor):
        return _mod.topk_forward(x.contiguous(), self.k)


def get_inputs():
    x = torch.randn(64, 8192, dtype=torch.float32)
    return [x]


def get_init_inputs():
    return [64, 8192, 8]

20260618_041709_claude_claude-opus-4-8_05_topk_bitonic