incorrect
49_Mamba2ReturnFinalState.py
started
2026-05-05T00:51:48Z
correctness issue: Output mismatch
timingkernelbench.timing
kernel mean / median / std
—
ref mean / median / std
6.02 / — / 0.02 μs
SOL (speed-of-light)nsight_hardware_counters
roofline / occupancynsight_hardware_counters
dram bandwidth
119.09 GB/s
warp stalls (top 5)nsight
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())