gpt-5.5-priority · original · l123_single_gpt
incorrect 49_Mamba2ReturnFinalState.py
model
gpt-5.5-priority
turns / max
1 / 1
tool calls
1
tokens (total)
0
wall clock
—
truncated
no
backend
cuda fp32
variant
original
runtime
—
ref runtime
6.02 μs
speedup
—
started
2026-05-05T00:51:48Z
correctness issue: Output mismatch
timingkernelbench.timing
kernel runtime
—
ref runtime
6.02 μs
speedup
—
kernel mean / median / std
—
ref mean / median / std
6.02 / — / 0.02 μs
SOL (speed-of-light)nsight_hardware_counters
SOL score (0–1)
0.035
dram util %
3.5%
compute util %
0.0%
bottleneck
unknown
dominant pipe
None
arithmetic intensity
—
ridge point
20.00
roofline / occupancynsight_hardware_counters
dram bandwidth
119.09 GB/s
dram util %
3.6%
fp32 tflops
—
fp32 util %
—
fp16 tflops
—
occupancy %
12.5%
bottleneck
unknown
dominant pipe %
—
arithmetic intensity
—
ridge point
20.00
L1 hit %
—
L2 hit %
14.3%
ld sectors / req
2.67
st sectors / req
—
regs / thread
255
smem / block
42496 B
block size
256
peak bw
3350.00 GB/s
peak fp32 tflops
67.00
peak fp16 tflops
1979.00
warp stalls (top 5)nsight
long_scoreboard
315.2%
short_scoreboard
289.6%
mio_throttle
1.0e-04%
reference PyTorch source (the prompt the agent sees)
import torch
import torch.nn as nn
import torch.nn.functional as F
from einops import rearrange

class Model(nn.Module):
    def __init__(self, batch_size, seq_length, n_heads, d_head, d_state, block_len=64):
        """
        Mamba Structured State Space model implementation for benchmarking.
        
        :param batch_size: Size of the batch
        :param seq_length: Length of the input sequence
        :param n_heads: Number of attention heads
        :param d_head: Dimension of each head
        :param d_state: Dimension of the state space
        :param block_len: Length of each block for chunked computation
        """
        super(Model, self).__init__()
        
        assert seq_length % block_len == 0, "Sequence length must be divisible by block length"
        
        self.batch_size = batch_size
        self.seq_length = seq_length
        self.n_heads = n_heads
        self.d_head = d_head
        self.d_state = d_state
        self.block_len = block_len
        
        # Initialize parameters
        self.A = nn.Parameter(torch.randn(batch_size, seq_length, n_heads))
        self.B = nn.Parameter(torch.randn(batch_size, seq_length, n_heads, d_state))
        self.C = nn.Parameter(torch.randn(batch_size, seq_length, n_heads, d_state))
        
    def segsum(self, x):
        """Naive segment sum calculation."""
        T = x.size(-1)
        x_cumsum = torch.cumsum(x, dim=-1)
        x_segsum = x_cumsum[..., :, None] - x_cumsum[..., None, :]
        mask = torch.tril(torch.ones(T, T, device=x.device, dtype=bool), diagonal=0)
        x_segsum = x_segsum.masked_fill(~mask, -torch.inf)
        return x_segsum
    
    def forward(self, X, initial_states=None):
        """
        Forward pass implementing the SSD operation.
        
        :param X: Input tensor of shape (batch, length, n_heads, d_head)
        :param initial_states: Optional initial states
        :return: Output tensor Y and final state
        """
        # Rearrange into blocks/chunks
        X_blocks, A_blocks, B_blocks, C_blocks = [
            rearrange(x, "b (c l) ... -> b c l ...", l=self.block_len)
            for x in (X, self.A, self.B, self.C)
        ]
        
        A_blocks = rearrange(A_blocks, "b c l h -> b h c l")
        A_cumsum = torch.cumsum(A_blocks, dim=-1)
        
        # 1. Compute diagonal block outputs
        L = torch.exp(self.segsum(A_blocks))
        Y_diag = torch.einsum("bclhn,bcshn,bhcls,bcshp->bclhp", 
                             C_blocks, B_blocks, L, X_blocks)
        
        # 2. Compute intra-chunk states
        decay_states = torch.exp((A_cumsum[:, :, :, -1:] - A_cumsum))
        states = torch.einsum("bclhn,bhcl,bclhp->bchpn", 
                            B_blocks, decay_states, X_blocks)
        
        # 3. Compute inter-chunk recurrence
        if initial_states is None:
            initial_states = torch.zeros_like(states[:, :1])
        states = torch.cat([initial_states, states], dim=1)
        
        decay_chunk = torch.exp(self.segsum(F.pad(A_cumsum[:, :, :, -1], (1, 0))))
        new_states = torch.einsum("bhzc,bchpn->bzhpn", decay_chunk, states)
        return new_states[:, -1]

# Test parameters
batch_size = 2048
seq_length = 128
n_heads = 8
d_head = 64
d_state = 16
block_len = 64

def get_inputs():
    return [torch.rand(batch_size, seq_length, n_heads, d_head)]

def get_init_inputs():
    return [batch_size, seq_length, n_heads, d_head, d_state, block_len]
turn 0 · FINAL · 1 tool call(s) · 62.8s turn · 62.8s LLM · 7,346 chars (click to expand)
turn 062.8s turn · 62.8s LLM · 0.0s tools · FINAL
reasoning
**Optimizing computations** I'm working with a pretty huge output, possibly around 2 million. I think the relative error for using expf could be around 1e-4, which might be acceptable. But should I use expf without the fast option? I wonder if NVCC's expf mapping would provide a faster approximation. Avoiding the --use_fast_math setting could be crucial. It seems like I need to optimize my computations and also consider shared memory indexing as part of this process.
call → submit_kernel
callsubmit_kernel { "kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\n_cpp_src = r\"\"\"\n#include <torch/extension.h>\n\ntorch::Tensor ssd_forward_none(torch::Tensor X, torch::Tensor A, torch::Tensor B);\ntorch::Tensor ssd_forward_init(torch::Tensor X, torch::Tensor A, torch::Tensor B, torch::Tensor initial);\n\"\"\"\n\n_cuda_src = r\"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <ATen/cuda/CUDAContext.h>\n\n#define MAX_L 128\n#define MAX_P 64\n#define MAX_N 16\n\ntemplate<bool HAS_INIT>\n__global__ void ssd_final_kernel(const float* __restrict__ X,\n const float* __restrict__ A,\n const float* __restrict__ Bpar,\n const float* __restrict__ init,\n float* __restrict__ Y,\n int Bsz, int L, int H, int P, int N) {\n int bh = blockIdx.x;\n int b = bh / H;\n int h = bh - b * H;\n int tid = threadIdx.x;\n\n __shared__ float Xsh[MAX_L * MAX_P];\n __shared__ float BDsh[MAX_L * MAX_N];\n __shared__ float decay[MAX_L];\n __shared__ float init_decay;\n\n // Load X while one thread computes the per-token decays from A.\n for (int i = tid; i < L * P; i += blockDim.x) {\n int t = i / P;\n int p = i - t * P;\n Xsh[i] = X[((b * L + t) * H + h) * P + p];\n }\n\n if (tid == 0) {\n float total = 0.0f;\n #pragma unroll\n for (int t = 0; t < MAX_L; ++t) {\n if (t < L) total += A[(b * L + t) * H + h];\n }\n init_decay = expf(total);\n float cs = 0.0f;\n #pragma unroll\n for (int t = 0; t < MAX_L; ++t) {\n if (t < L) {\n cs += A[(b * L + t) * H + h];\n decay[t] = expf(total - cs);\n }\n }\n }\n __syncthreads();\n\n // Pre-scale B by the decay. This leaves the main loop as a small GEMM:\n // Y[p,n] = sum_t X[t,p] * (B[t,n] * decay[t]).\n for (int i = tid; i < L * N; i += blockDim.x) {\n int t = i / N;\n int n = i - t * N;\n BDsh[i] = Bpar[((b * L + t) * H + h) * N + n] * decay[t];\n }\n __syncthreads();\n\n for (int idx = tid; idx < P * N; idx += blockDim.x) {\n int p = idx / N;\n int n = idx - p * N;\n float acc = 0.0f;\n #pragma unroll\n for (int t = 0; t < MAX_L; ++t) {\n if (t < L) acc = fmaf(Xsh[t * P + p], BDsh[t * N + n], acc);\n }\n if (HAS_INIT) {\n // Reference expects initial_states with shape [B, 1, H, P, N].\n acc = fmaf(init[((b * H + h) * P + p) * N + n], init_decay, acc);\n }\n Y[((b * H + h) * P + p) * N + n] = acc;\n }\n}\n\ntorch::Tensor ssd_forward_none(torch::Tensor X, torch::Tensor A, torch::Tensor Bpar) {\n const int Bsz = (int)X.size(0);\n const int L = (int)X.size(1);\n const int H = (int)X.size(2);\n const int P = (int)X.size(3);\n const int N = (int)Bpar.size(3);\n auto Y = torch::empty({Bsz, H, P, N}, X.options());\n const int threads = 256;\n const dim3 blocks(Bsz * H);\n cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n ssd_final_kernel<false><<<blocks, threads, 0, stream>>>(\n X.data_ptr<float>(), A.data_ptr<float>(), Bpar.data_ptr<float>(), nullptr,\n Y.data_ptr<float>(), Bsz, L, H, P, N);\n return Y;\n}\n\ntorch::Tensor ssd_forward_init(torch::Tensor X, torch::Tensor A, torch::Tensor Bpar, torch::Tensor initial) {\n const int Bsz = (int)X.size(0);\n const int L = (int)X.size(1);\n const int H = (int)X.size(2);\n const int P = (int)X.size(3);\n const int N = (int)Bpar.size(3);\n auto Y = torch::empty({Bsz, H, P, N}, X.options());\n const int threads = 256;\n const dim3 blocks(Bsz * H);\n cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n ssd_final_kernel<true><<<blocks, threads, 0, stream>>>(\n X.data_ptr<float>(), A.data_ptr<float>(), Bpar.data_ptr<float>(), initial.data_ptr<float>(),\n Y.data_ptr<float>(), Bsz, L, H, P, N);\n return Y;\n}\n\"\"\"\n\n_ext = load_inline(\n name=\"ssd_final_ext_v1\",\n cpp_sources=_cpp_src,\n cuda_sources=_cuda_src,\n functions=[\"ssd_forward_none\", \"ssd_forward_init\"],\n extra_cflags=[\"-O3\"],\n extra_cuda_cflags=[\"-O3\"],\n verbose=False,\n)\n\nclass ModelNew(nn.Module):\n def __init__(self, batch_size, seq_length, n_heads, d_head, d_state, block_len=64):\n super(ModelNew, self).__init__()\n assert seq_length % block_len == 0, \"Sequence length must be divisible by block length\"\n self.batch_size = batch_size\n self.seq_length = seq_length\n self.n_heads = n_heads\n self.d_head = d_head\n self.d_state = d_state\n self.block_len = block_len\n self.A = nn.Parameter(torch.randn(batch_size, seq_length, n_heads))\n self.B = nn.Parameter(torch.randn(batch_size, seq_length, n_heads, d_state))\n self.C = nn.Parameter(torch.randn(batch_size, seq_length, n_heads, d_state))\n\n def forward(self, X, initial_states=None):\n if initial_states is None:\n return _ext.ssd_forward_none(X.contiguous(), self.A.contiguous(), self.B.contiguous())\n else:\n return _ext.ssd_forward_init(X.contiguous(), self.A.contiguous(), self.B.contiguous(), initial_states.contiguous())\n" }
result ← submit_kernel (fail)
failsubmit_kernel submit_kernel FAILED: correctness check did not pass ((0 / 5) trials). correctness_issue: Output mismatch max_difference: ['439804651110400.000000', '439804651110400.000000', '426610511577088.000000', '417814418554880.000000', '417814418554880.000000'] avg_difference: ['10690443264.000000', '10944868352.000000', '11044740096.000000', '10977667072.000000', '10536970240.000000']
final submitted kernel (151 lines, 5,330 chars) — full source, untruncated
import torch
import torch.nn as nn
from torch.utils.cpp_extension import load_inline

_cpp_src = r"""
#include <torch/extension.h>

torch::Tensor ssd_forward_none(torch::Tensor X, torch::Tensor A, torch::Tensor B);
torch::Tensor ssd_forward_init(torch::Tensor X, torch::Tensor A, torch::Tensor B, torch::Tensor initial);
"""

_cuda_src = r"""
#include <torch/extension.h>
#include <cuda.h>
#include <cuda_runtime.h>
#include <ATen/cuda/CUDAContext.h>

#define MAX_L 128
#define MAX_P 64
#define MAX_N 16

template<bool HAS_INIT>
__global__ void ssd_final_kernel(const float* __restrict__ X,
                                 const float* __restrict__ A,
                                 const float* __restrict__ Bpar,
                                 const float* __restrict__ init,
                                 float* __restrict__ Y,
                                 int Bsz, int L, int H, int P, int N) {
    int bh = blockIdx.x;
    int b = bh / H;
    int h = bh - b * H;
    int tid = threadIdx.x;

    __shared__ float Xsh[MAX_L * MAX_P];
    __shared__ float BDsh[MAX_L * MAX_N];
    __shared__ float decay[MAX_L];
    __shared__ float init_decay;

    // Load X while one thread computes the per-token decays from A.
    for (int i = tid; i < L * P; i += blockDim.x) {
        int t = i / P;
        int p = i - t * P;
        Xsh[i] = X[((b * L + t) * H + h) * P + p];
    }

    if (tid == 0) {
        float total = 0.0f;
        #pragma unroll
        for (int t = 0; t < MAX_L; ++t) {
            if (t < L) total += A[(b * L + t) * H + h];
        }
        init_decay = expf(total);
        float cs = 0.0f;
        #pragma unroll
        for (int t = 0; t < MAX_L; ++t) {
            if (t < L) {
                cs += A[(b * L + t) * H + h];
                decay[t] = expf(total - cs);
            }
        }
    }
    __syncthreads();

    // Pre-scale B by the decay.  This leaves the main loop as a small GEMM:
    // Y[p,n] = sum_t X[t,p] * (B[t,n] * decay[t]).
    for (int i = tid; i < L * N; i += blockDim.x) {
        int t = i / N;
        int n = i - t * N;
        BDsh[i] = Bpar[((b * L + t) * H + h) * N + n] * decay[t];
    }
    __syncthreads();

    for (int idx = tid; idx < P * N; idx += blockDim.x) {
        int p = idx / N;
        int n = idx - p * N;
        float acc = 0.0f;
        #pragma unroll
        for (int t = 0; t < MAX_L; ++t) {
            if (t < L) acc = fmaf(Xsh[t * P + p], BDsh[t * N + n], acc);
        }
        if (HAS_INIT) {
            // Reference expects initial_states with shape [B, 1, H, P, N].
            acc = fmaf(init[((b * H + h) * P + p) * N + n], init_decay, acc);
        }
        Y[((b * H + h) * P + p) * N + n] = acc;
    }
}

torch::Tensor ssd_forward_none(torch::Tensor X, torch::Tensor A, torch::Tensor Bpar) {
    const int Bsz = (int)X.size(0);
    const int L   = (int)X.size(1);
    const int H   = (int)X.size(2);
    const int P   = (int)X.size(3);
    const int N   = (int)Bpar.size(3);
    auto Y = torch::empty({Bsz, H, P, N}, X.options());
    const int threads = 256;
    const dim3 blocks(Bsz * H);
    cudaStream_t stream = at::cuda::getCurrentCUDAStream();
    ssd_final_kernel<false><<<blocks, threads, 0, stream>>>(
        X.data_ptr<float>(), A.data_ptr<float>(), Bpar.data_ptr<float>(), nullptr,
        Y.data_ptr<float>(), Bsz, L, H, P, N);
    return Y;
}

torch::Tensor ssd_forward_init(torch::Tensor X, torch::Tensor A, torch::Tensor Bpar, torch::Tensor initial) {
    const int Bsz = (int)X.size(0);
    const int L   = (int)X.size(1);
    const int H   = (int)X.size(2);
    const int P   = (int)X.size(3);
    const int N   = (int)Bpar.size(3);
    auto Y = torch::empty({Bsz, H, P, N}, X.options());
    const int threads = 256;
    const dim3 blocks(Bsz * H);
    cudaStream_t stream = at::cuda::getCurrentCUDAStream();
    ssd_final_kernel<true><<<blocks, threads, 0, stream>>>(
        X.data_ptr<float>(), A.data_ptr<float>(), Bpar.data_ptr<float>(), initial.data_ptr<float>(),
        Y.data_ptr<float>(), Bsz, L, H, P, N);
    return Y;
}
"""

_ext = load_inline(
    name="ssd_final_ext_v1",
    cpp_sources=_cpp_src,
    cuda_sources=_cuda_src,
    functions=["ssd_forward_none", "ssd_forward_init"],
    extra_cflags=["-O3"],
    extra_cuda_cflags=["-O3"],
    verbose=False,
)

class ModelNew(nn.Module):
    def __init__(self, batch_size, seq_length, n_heads, d_head, d_state, block_len=64):
        super(ModelNew, self).__init__()
        assert seq_length % block_len == 0, "Sequence length must be divisible by block length"
        self.batch_size = batch_size
        self.seq_length = seq_length
        self.n_heads = n_heads
        self.d_head = d_head
        self.d_state = d_state
        self.block_len = block_len
        self.A = nn.Parameter(torch.randn(batch_size, seq_length, n_heads))
        self.B = nn.Parameter(torch.randn(batch_size, seq_length, n_heads, d_state))
        self.C = nn.Parameter(torch.randn(batch_size, seq_length, n_heads, d_state))

    def forward(self, X, initial_states=None):
        if initial_states is None:
            return _ext.ssd_forward_none(X.contiguous(), self.A.contiguous(), self.B.contiguous())
        else:
            return _ext.ssd_forward_init(X.contiguous(), self.A.contiguous(), self.B.contiguous(), initial_states.contiguous())