correct
30_SelectiveScan_Mamba.py
started
2026-05-05T00:32:21Z
timingkernelbench.timing
kernel mean / median / std
0.84 / — / 0.01 μs
ref mean / median / std
135.00 / — / 186.00 μs
numerical precisioneval_kernel_against_ref
gpu memoryextended_metrics.measure_memory
kernel launches / fusiontorch.profiler
fusion ratio (ref / kernel)
952.89x
energyNVML (pynvml)
ratio (ref / kernel)
29.80x
SOL (speed-of-light)nsight_hardware_counters
roofline / occupancynsight_hardware_counters
dram bandwidth
6279.98 GB/s
warp stalls (top 5)nsight
reference PyTorch source (the prompt the agent sees)
import torch
import torch.nn as nn
class Model(nn.Module):
"""
Selective scan (Mamba's S6 layer): projects input to derive input-dependent SSM parameters
(delta, B, C), discretizes a learned state matrix A, runs the linear recurrence, and
applies a skip connection via D. The fusion opportunity is keeping all intermediates
(discretized parameters, hidden states) in SRAM rather than materializing them to global memory.
Args:
d_model (int): Input feature dimension.
d_state (int): Hidden state dimension per channel (N in Mamba, typically 16).
"""
def __init__(self, d_model: int, d_state: int):
super(Model, self).__init__()
self.d_model = d_model
self.d_state = d_state
self.A_log = nn.Parameter(torch.randn(d_model, d_state))
self.proj_B = nn.Linear(d_model, d_state, bias=False)
self.proj_C = nn.Linear(d_model, d_state, bias=False)
self.proj_delta = nn.Linear(d_model, d_model, bias=True)
self.D = nn.Parameter(torch.ones(d_model))
def forward(self, x: torch.Tensor) -> torch.Tensor:
B, L, D = x.shape
N = self.d_state
delta = torch.nn.functional.softplus(self.proj_delta(x))
B_t = self.proj_B(x)
C_t = self.proj_C(x)
A = -torch.exp(self.A_log)
dA = torch.exp(delta.unsqueeze(-1) * A.unsqueeze(0).unsqueeze(0))
dB = delta.unsqueeze(-1) * B_t.unsqueeze(2)
x_db = x.unsqueeze(-1) * dB
h = torch.zeros(B, D, N, device=x.device, dtype=x.dtype)
outputs = []
for t in range(L):
h = dA[:, t] * h + x_db[:, t]
y_t = (h * C_t[:, t].unsqueeze(1)).sum(dim=-1)
outputs.append(y_t)
y = torch.stack(outputs, dim=1)
y = y + x * self.D.unsqueeze(0).unsqueeze(0)
return y
batch_size = 16
d_model = 256
d_state = 16
seq_len = 2048
def get_inputs():
return [torch.randn(popcorn_pri.jitter_int(batch_size), popcorn_pri.jitter_int(seq_len), d_model)]
def get_init_inputs():
return [d_model, d_state]turn 0 · FINAL · 1 tool call(s) · 74.4s turn · 74.4s LLM · 6,735 chars (click to expand)
turn 074.4s turn · 74.4s LLM · 0.0s tools · FINAL
reasoning
**Evaluating tile handling**
I'm trying to figure out if D is not a multiple of TILE, and I need to check if the shape C_t is linear, which should be in the format of [B, L, N]. I'm also considering if everything is contiguous. This seems a bit complicated, but I want to make sure I understand the requirements clearly. It’s all about ensuring that I'm correctly handling those shapes and conditions for the task ahead!
call → submit_kernel
callsubmit_kernel
{
"kernel_code": "import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.utils.cpp_extension import load_inline\n\n\n_cpp_src = r'''\n#include <torch/extension.h>\n\ntorch::Tensor selective_scan_forward(torch::Tensor x,\n torch::Tensor delta,\n torch::Tensor B_t,\n torch::Tensor C_t,\n torch::Tensor A_log,\n torch::Tensor D_param);\n'''\n\n_cuda_src = r'''\n#include <torch/extension.h>\n#include <ATen/cuda/CUDAContext.h>\n#include <c10/cuda/CUDAException.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n\n#ifndef TILE_D\n#define TILE_D 8\n#endif\n#ifndef STATE_W\n#define STATE_W 16\n#endif\n\n__global__ __launch_bounds__(TILE_D * STATE_W, 2)\nvoid selective_scan_kernel(const float* __restrict__ x,\n const float* __restrict__ delta,\n const float* __restrict__ B_t,\n const float* __restrict__ C_t,\n const float* __restrict__ A_log,\n const float* __restrict__ D_param,\n float* __restrict__ y,\n int B, int L, int D, int N) {\n const int b = (int)blockIdx.y;\n const int d_base = (int)blockIdx.x * TILE_D;\n const int tid = (int)threadIdx.x;\n const int kd = tid >> 4; // channel within the tile\n const int n = tid & 15; // state lane\n const int d = d_base + kd;\n\n const int lane_in_warp = tid & 31;\n const unsigned mask = 0xFFFFu << (lane_in_warp & 16);\n\n float h = 0.0f;\n float a = 0.0f;\n if (d < D && n < N) {\n a = -expf(A_log[d * N + n]);\n }\n\n float d_skip = 0.0f;\n if (d < D && n == 0) d_skip = D_param[d];\n\n const int bd_x_base = (b * L) * D;\n const int bd_s_base = (b * L) * N;\n\n for (int t = 0; t < L; ++t) {\n float xv = 0.0f;\n float dt = 0.0f;\n const int x_idx = bd_x_base + t * D + d;\n if (d < D && n == 0) {\n xv = x[x_idx];\n dt = delta[x_idx];\n }\n xv = __shfl_sync(mask, xv, 0, STATE_W);\n dt = __shfl_sync(mask, dt, 0, STATE_W);\n\n float acc = 0.0f;\n if (d < D && n < N) {\n const int s_idx = bd_s_base + t * N + n;\n const float bn = B_t[s_idx];\n const float cn = C_t[s_idx];\n const float ea = expf(dt * a);\n h = fmaf(xv * dt, bn, ea * h);\n acc = h * cn;\n }\n\n acc += __shfl_down_sync(mask, acc, 8, STATE_W);\n acc += __shfl_down_sync(mask, acc, 4, STATE_W);\n acc += __shfl_down_sync(mask, acc, 2, STATE_W);\n acc += __shfl_down_sync(mask, acc, 1, STATE_W);\n\n if (d < D && n == 0) {\n y[x_idx] = acc + xv * d_skip;\n }\n }\n}\n\ntorch::Tensor selective_scan_forward(torch::Tensor x,\n torch::Tensor delta,\n torch::Tensor B_t,\n torch::Tensor C_t,\n torch::Tensor A_log,\n torch::Tensor D_param) {\n const int B = (int)x.size(0);\n const int L = (int)x.size(1);\n const int D = (int)x.size(2);\n const int N = (int)A_log.size(1);\n\n auto y = torch::empty_like(x);\n dim3 block(TILE_D * STATE_W);\n dim3 grid((D + TILE_D - 1) / TILE_D, B);\n\n selective_scan_kernel<<<grid, block, 0, at::cuda::getCurrentCUDAStream()>>>(\n x.data_ptr<float>(), delta.data_ptr<float>(), B_t.data_ptr<float>(), C_t.data_ptr<float>(),\n A_log.data_ptr<float>(), D_param.data_ptr<float>(), y.data_ptr<float>(), B, L, D, N);\n C10_CUDA_KERNEL_LAUNCH_CHECK();\n return y;\n}\n'''\n\n_ext = load_inline(\n name='selective_scan_mamba_s6_ext_v1',\n cpp_sources=_cpp_src,\n cuda_sources=_cuda_src,\n functions=['selective_scan_forward'],\n extra_cflags=['-O3'],\n extra_cuda_cflags=['-O3', '--use_fast_math'],\n verbose=False,\n)\n\n\nclass ModelNew(nn.Module):\n def __init__(self, d_model: int, d_state: int):\n super(ModelNew, self).__init__()\n self.d_model = d_model\n self.d_state = d_state\n self.A_log = nn.Parameter(torch.randn(d_model, d_state))\n self.proj_B = nn.Linear(d_model, d_state, bias=False)\n self.proj_C = nn.Linear(d_model, d_state, bias=False)\n self.proj_delta = nn.Linear(d_model, d_model, bias=True)\n self.D = nn.Parameter(torch.ones(d_model))\n\n def forward(self, x: torch.Tensor) -> torch.Tensor:\n delta = F.softplus(self.proj_delta(x))\n B_t = self.proj_B(x)\n C_t = self.proj_C(x)\n return _ext.selective_scan_forward(x, delta, B_t, C_t, self.A_log, self.D)\n"
}
result ← submit_kernel (ok)
oksubmit_kernel
submit_kernel PASSED: (5 / 5) correctness trials all passed.
Kernel runtime: 0.84 μs
Runtime stats: mean=0.84μs median=?μs std=0.01μs
Numerical precision: max_abs_err=2.67e-05 mean_abs_err=1.45e-07 max_rel_err=2.38e+05
Memory: 133.6 MB peak (ref: 1765.86 MB, ratio: 0.0757x)
Kernel launches: 28 (ref: 26681, fusion ratio: 952.8929)
Energy: 352.66 mJ/run (ref: 10509.44 mJ, ratio: 29.8005x)
SOL score: 1.0
Flagged for excessive speedup — this submission may have been rejected by automated review.
final submitted kernel (145 lines, 4,776 chars) — full source, untruncated
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.cpp_extension import load_inline
_cpp_src = r'''
#include <torch/extension.h>
torch::Tensor selective_scan_forward(torch::Tensor x,
torch::Tensor delta,
torch::Tensor B_t,
torch::Tensor C_t,
torch::Tensor A_log,
torch::Tensor D_param);
'''
_cuda_src = r'''
#include <torch/extension.h>
#include <ATen/cuda/CUDAContext.h>
#include <c10/cuda/CUDAException.h>
#include <cuda.h>
#include <cuda_runtime.h>
#ifndef TILE_D
#define TILE_D 8
#endif
#ifndef STATE_W
#define STATE_W 16
#endif
__global__ __launch_bounds__(TILE_D * STATE_W, 2)
void selective_scan_kernel(const float* __restrict__ x,
const float* __restrict__ delta,
const float* __restrict__ B_t,
const float* __restrict__ C_t,
const float* __restrict__ A_log,
const float* __restrict__ D_param,
float* __restrict__ y,
int B, int L, int D, int N) {
const int b = (int)blockIdx.y;
const int d_base = (int)blockIdx.x * TILE_D;
const int tid = (int)threadIdx.x;
const int kd = tid >> 4; // channel within the tile
const int n = tid & 15; // state lane
const int d = d_base + kd;
const int lane_in_warp = tid & 31;
const unsigned mask = 0xFFFFu << (lane_in_warp & 16);
float h = 0.0f;
float a = 0.0f;
if (d < D && n < N) {
a = -expf(A_log[d * N + n]);
}
float d_skip = 0.0f;
if (d < D && n == 0) d_skip = D_param[d];
const int bd_x_base = (b * L) * D;
const int bd_s_base = (b * L) * N;
for (int t = 0; t < L; ++t) {
float xv = 0.0f;
float dt = 0.0f;
const int x_idx = bd_x_base + t * D + d;
if (d < D && n == 0) {
xv = x[x_idx];
dt = delta[x_idx];
}
xv = __shfl_sync(mask, xv, 0, STATE_W);
dt = __shfl_sync(mask, dt, 0, STATE_W);
float acc = 0.0f;
if (d < D && n < N) {
const int s_idx = bd_s_base + t * N + n;
const float bn = B_t[s_idx];
const float cn = C_t[s_idx];
const float ea = expf(dt * a);
h = fmaf(xv * dt, bn, ea * h);
acc = h * cn;
}
acc += __shfl_down_sync(mask, acc, 8, STATE_W);
acc += __shfl_down_sync(mask, acc, 4, STATE_W);
acc += __shfl_down_sync(mask, acc, 2, STATE_W);
acc += __shfl_down_sync(mask, acc, 1, STATE_W);
if (d < D && n == 0) {
y[x_idx] = acc + xv * d_skip;
}
}
}
torch::Tensor selective_scan_forward(torch::Tensor x,
torch::Tensor delta,
torch::Tensor B_t,
torch::Tensor C_t,
torch::Tensor A_log,
torch::Tensor D_param) {
const int B = (int)x.size(0);
const int L = (int)x.size(1);
const int D = (int)x.size(2);
const int N = (int)A_log.size(1);
auto y = torch::empty_like(x);
dim3 block(TILE_D * STATE_W);
dim3 grid((D + TILE_D - 1) / TILE_D, B);
selective_scan_kernel<<<grid, block, 0, at::cuda::getCurrentCUDAStream()>>>(
x.data_ptr<float>(), delta.data_ptr<float>(), B_t.data_ptr<float>(), C_t.data_ptr<float>(),
A_log.data_ptr<float>(), D_param.data_ptr<float>(), y.data_ptr<float>(), B, L, D, N);
C10_CUDA_KERNEL_LAUNCH_CHECK();
return y;
}
'''
_ext = load_inline(
name='selective_scan_mamba_s6_ext_v1',
cpp_sources=_cpp_src,
cuda_sources=_cuda_src,
functions=['selective_scan_forward'],
extra_cflags=['-O3'],
extra_cuda_cflags=['-O3', '--use_fast_math'],
verbose=False,
)
class ModelNew(nn.Module):
def __init__(self, d_model: int, d_state: int):
super(ModelNew, self).__init__()
self.d_model = d_model
self.d_state = d_state
self.A_log = nn.Parameter(torch.randn(d_model, d_state))
self.proj_B = nn.Linear(d_model, d_state, bias=False)
self.proj_C = nn.Linear(d_model, d_state, bias=False)
self.proj_delta = nn.Linear(d_model, d_model, bias=True)
self.D = nn.Parameter(torch.ones(d_model))
def forward(self, x: torch.Tensor) -> torch.Tensor:
delta = F.softplus(self.proj_delta(x))
B_t = self.proj_B(x)
C_t = self.proj_C(x)
return _ext.selective_scan_forward(x, delta, B_t, C_t, self.A_log, self.D)