gpt-5.5-priority · popcorn · l123_single_gpt
correct 4_MambaBlock.py
model
gpt-5.5-priority
turns / max
1 / 1
tool calls
1
tokens (total)
0
wall clock
—
truncated
no
backend
cuda fp32
variant
popcorn
runtime
3.08 μs
ref runtime
161.00 μs
speedup
52.27x
started
2026-05-05T00:01:59Z
timingkernelbench.timing
kernel runtime
3.08 μs
ref runtime
161.00 μs
speedup
52.27x
kernel mean / median / std
3.08 / — / 0.01 μs
ref mean / median / std
161.00 / — / 399.00 μs
numerical precisioneval_kernel_against_ref
max abs error
4.77e-07
mean abs error
4.52e-09
max rel error
0.15
mean rel error
6.35e-08
gpu memoryextended_metrics.measure_memory
kernel peak
554.22 MB
ref peak
3690.75 MB
kernel / ref
0.15x
kernel launches / fusiontorch.profiler
kernel launches
59
ref launches
26754
fusion ratio (ref / kernel)
453.46x
total cuda time
4026 μs
energyNVML (pynvml)
kernel mJ / run
1592.10
ref mJ / run
13055.16
ratio (ref / kernel)
8.20x
avg power
519.09 W
ref avg power
174.98 W
SOL (speed-of-light)nsight_hardware_counters
SOL score (0–1)
1.000
dram util %
100.0%
compute util %
13.6%
bottleneck
memory-bound
dominant pipe
tensor-core
arithmetic intensity
—
ridge point
295.22
roofline / occupancynsight_hardware_counters
dram bandwidth
18902.03 GB/s
dram util %
100.0%
fp32 tflops
—
fp32 util %
—
fp16 tflops
—
occupancy %
654.6%
bottleneck
memory-bound
dominant pipe %
13.6%
arithmetic intensity
—
ridge point
295.22
L1 hit %
—
L2 hit %
697.3%
ld sectors / req
98.83
st sectors / req
—
regs / thread
1226
smem / block
143744 B
block size
1664
peak bw
3350.00 GB/s
peak fp32 tflops
67.00
peak fp16 tflops
1979.00
warp stalls (top 5)nsight
long_scoreboard
51936.4%
short_scoreboard
5303.2%
mio_throttle
4660.3%
flagssubmit_kernel guard
excessive speedup
flagged
reference PyTorch source (the prompt the agent sees)
import torch
import torch.nn as nn

class Model(nn.Module):
    """
    Full Mamba block: RMSNorm -> linear expansion into two branches -> causal depthwise Conv1d ->
    SiLU -> selective scan (input-dependent SSM) -> elementwise gate -> linear projection -> residual.
    This is the complete Mamba architecture block as described in Gu & Dao (2023), used in Mamba,
    Caduceus (bidirectional DNA), and various protein/genomic language models.

    Args:
        d_model (int): Model dimension.
        d_state (int): SSM state dimension (N).
        d_conv (int): Causal convolution kernel size.
        expand (int): Expansion factor for inner dimension.
    """
    def __init__(self, d_model: int, d_state: int, d_conv: int, expand: int):
        super(Model, self).__init__()
        self.d_model = d_model
        self.d_inner = d_model * expand
        self.d_state = d_state
        self.d_conv = d_conv

        self.norm = nn.RMSNorm(d_model)
        self.in_proj = nn.Linear(d_model, self.d_inner * 2, bias=False)
        self.conv1d = nn.Conv1d(self.d_inner, self.d_inner, d_conv,
                                padding=d_conv - 1, groups=self.d_inner, bias=True)
        self.activation = nn.SiLU()

        self.A_log = nn.Parameter(torch.randn(self.d_inner, d_state))
        self.proj_B = nn.Linear(self.d_inner, d_state, bias=False)
        self.proj_C = nn.Linear(self.d_inner, d_state, bias=False)
        self.proj_delta = nn.Linear(self.d_inner, self.d_inner, bias=True)
        self.D = nn.Parameter(torch.ones(self.d_inner))

        self.out_proj = nn.Linear(self.d_inner, d_model, bias=False)

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        B, L, D = x.shape
        residual = x
        x = self.norm(x)

        xz = self.in_proj(x)
        x_branch, z = xz.chunk(2, dim=-1)

        x_branch = x_branch.transpose(1, 2)
        x_branch = self.conv1d(x_branch)[..., :L]
        x_branch = x_branch.transpose(1, 2)
        x_branch = self.activation(x_branch)

        N = self.d_state
        delta = torch.nn.functional.softplus(self.proj_delta(x_branch))
        B_t = self.proj_B(x_branch)
        C_t = self.proj_C(x_branch)

        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_branch.unsqueeze(-1) * dB

        h = torch.zeros(B, self.d_inner, 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_branch * self.D.unsqueeze(0).unsqueeze(0)

        z = self.activation(z)
        y = y * z
        y = self.out_proj(y)
        y = y + residual
        return y

batch_size = 16
d_model = 256
d_state = 16
d_conv = 4
expand = 2
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, d_conv, expand]
turn 0 · FINAL · 1 tool call(s) · 108.1s turn · 108.1s LLM · 8,859 chars (click to expand)
turn 0108.1s turn · 108.1s LLM · 0.0s tools · FINAL
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_cpp_src = r\"\"\"\n#include <torch/extension.h>\n\ntorch::Tensor causal_conv_silu_forward(torch::Tensor x, torch::Tensor w, torch::Tensor bias);\ntorch::Tensor selective_scan_forward(torch::Tensor x, torch::Tensor delta, torch::Tensor Bt, torch::Tensor Ct, torch::Tensor A_log, torch::Tensor D);\n\"\"\"\n\n_cuda_src = r\"\"\"\n#include <torch/extension.h>\n#include <ATen/cuda/CUDAContext.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <math.h>\n\n__global__ void causal_conv_silu_kernel(\n const float* __restrict__ x,\n const float* __restrict__ w,\n const float* __restrict__ bias,\n float* __restrict__ out,\n long sB, long sL, long sI,\n int B, int L, int I, int K)\n{\n long total = (long)B * L * I;\n long idx = (long)blockIdx.x * blockDim.x + threadIdx.x;\n long stride = (long)blockDim.x * gridDim.x;\n for (; idx < total; idx += stride) {\n int i = (int)(idx % I);\n long tmp = idx / I;\n int t = (int)(tmp % L);\n int b = (int)(tmp / L);\n float acc = bias[i];\n // PyTorch Conv1d with padding K-1, returning the first L positions:\n // input index = t + k - (K-1).\n #pragma unroll\n for (int k = 0; k < 8; ++k) {\n if (k < K) {\n int ti = t + k - (K - 1);\n if (ti >= 0) {\n acc += x[(long)b * sB + (long)ti * sL + (long)i * sI] * w[(long)i * K + k];\n }\n }\n }\n float sig = 1.0f / (1.0f + expf(-acc));\n out[idx] = acc * sig;\n }\n}\n\n__global__ void selective_scan_kernel(\n const float* __restrict__ x,\n const float* __restrict__ delta,\n const float* __restrict__ Bt,\n const float* __restrict__ Ct,\n const float* __restrict__ A_log,\n const float* __restrict__ D,\n float* __restrict__ y,\n int B, int L, int I, int N)\n{\n int lane = threadIdx.x & 31;\n int warp_in_block = threadIdx.x >> 5;\n int warps_per_block = blockDim.x >> 5;\n int wi = blockIdx.x * warps_per_block + warp_in_block;\n int total_warps = B * I;\n if (wi >= total_warps) return;\n\n int b = wi / I;\n int i = wi - b * I;\n int n = lane;\n\n float h = 0.0f;\n float aval = 0.0f;\n if (n < N) {\n aval = -expf(A_log[(long)i * N + n]);\n }\n float dskip = D[i];\n\n for (int t = 0; t < L; ++t) {\n long xi = ((long)b * L + t) * I + i;\n float xv = x[xi];\n float del = delta[xi];\n float prod = 0.0f;\n if (n < N) {\n long bcn = ((long)b * L + t) * N + n;\n float da = expf(del * aval);\n h = da * h + (xv * del) * Bt[bcn];\n prod = h * Ct[bcn];\n }\n // warp reduction over the N=16 active lanes (remaining lanes hold zero)\n prod += __shfl_down_sync(0xffffffff, prod, 16);\n prod += __shfl_down_sync(0xffffffff, prod, 8);\n prod += __shfl_down_sync(0xffffffff, prod, 4);\n prod += __shfl_down_sync(0xffffffff, prod, 2);\n prod += __shfl_down_sync(0xffffffff, prod, 1);\n if (lane == 0) {\n y[xi] = prod + xv * dskip;\n }\n }\n}\n\ntorch::Tensor causal_conv_silu_forward(torch::Tensor x, torch::Tensor w, torch::Tensor bias) {\n TORCH_CHECK(x.is_cuda() && w.is_cuda() && bias.is_cuda(), \"tensors must be CUDA\");\n TORCH_CHECK(x.scalar_type() == at::kFloat, \"only float32 is supported\");\n TORCH_CHECK(w.scalar_type() == at::kFloat && bias.scalar_type() == at::kFloat, \"only float32 is supported\");\n int B = (int)x.size(0);\n int L = (int)x.size(1);\n int I = (int)x.size(2);\n int K = (int)w.size(2);\n auto out = torch::empty({B, L, I}, x.options());\n long total = (long)B * L * I;\n int threads = 256;\n int blocks = (int)((total + threads - 1) / threads);\n if (blocks > 65535) blocks = 65535;\n causal_conv_silu_kernel<<<blocks, threads, 0, at::cuda::getCurrentCUDAStream()>>>(\n x.data_ptr<float>(), w.data_ptr<float>(), bias.data_ptr<float>(), out.data_ptr<float>(),\n x.stride(0), x.stride(1), x.stride(2), B, L, I, K);\n return out;\n}\n\ntorch::Tensor selective_scan_forward(torch::Tensor x, torch::Tensor delta, torch::Tensor Bt, torch::Tensor Ct, torch::Tensor A_log, torch::Tensor D) {\n TORCH_CHECK(x.is_cuda() && delta.is_cuda() && Bt.is_cuda() && Ct.is_cuda() && A_log.is_cuda() && D.is_cuda(), \"tensors must be CUDA\");\n TORCH_CHECK(x.scalar_type() == at::kFloat, \"only float32 is supported\");\n int B = (int)x.size(0);\n int L = (int)x.size(1);\n int I = (int)x.size(2);\n int N = (int)Bt.size(2);\n auto y = torch::empty_like(x);\n int threads = 128; // 4 warps; each warp scans one (batch, channel)\n int warps_per_block = threads / 32;\n int blocks = (B * I + warps_per_block - 1) / warps_per_block;\n selective_scan_kernel<<<blocks, threads, 0, at::cuda::getCurrentCUDAStream()>>>(\n x.data_ptr<float>(), delta.data_ptr<float>(), Bt.data_ptr<float>(), Ct.data_ptr<float>(),\n A_log.data_ptr<float>(), D.data_ptr<float>(), y.data_ptr<float>(), B, L, I, N);\n return y;\n}\n\"\"\"\n\n_mamba_ext = load_inline(\n name=\"mamba_block_custom_ext_v1\",\n cpp_sources=_cpp_src,\n cuda_sources=_cuda_src,\n functions=[\"causal_conv_silu_forward\", \"selective_scan_forward\"],\n extra_cflags=[\"-O3\"],\n extra_cuda_cflags=[\"-O3\"],\n verbose=False,\n)\n\nclass ModelNew(nn.Module):\n def __init__(self, d_model: int, d_state: int, d_conv: int, expand: int):\n super(ModelNew, self).__init__()\n self.d_model = d_model\n self.d_inner = d_model * expand\n self.d_state = d_state\n self.d_conv = d_conv\n\n self.norm = nn.RMSNorm(d_model)\n self.in_proj = nn.Linear(d_model, self.d_inner * 2, bias=False)\n self.conv1d = nn.Conv1d(self.d_inner, self.d_inner, d_conv,\n padding=d_conv - 1, groups=self.d_inner, bias=True)\n self.activation = nn.SiLU()\n\n self.A_log = nn.Parameter(torch.randn(self.d_inner, d_state))\n self.proj_B = nn.Linear(self.d_inner, d_state, bias=False)\n self.proj_C = nn.Linear(self.d_inner, d_state, bias=False)\n self.proj_delta = nn.Linear(self.d_inner, self.d_inner, bias=True)\n self.D = nn.Parameter(torch.ones(self.d_inner))\n\n self.out_proj = nn.Linear(self.d_inner, d_model, bias=False)\n\n def forward(self, x: torch.Tensor) -> torch.Tensor:\n residual = x\n x_norm = self.norm(x)\n xz = F.linear(x_norm, self.in_proj.weight, None)\n I = self.d_inner\n x_branch = xz[..., :I]\n z = xz[..., I:]\n\n # Fused causal depthwise convolution over the x branch and SiLU activation.\n x_branch = _mamba_ext.causal_conv_silu_forward(x_branch, self.conv1d.weight, self.conv1d.bias)\n\n delta = F.softplus(F.linear(x_branch, self.proj_delta.weight, self.proj_delta.bias))\n B_t = F.linear(x_branch, self.proj_B.weight, None)\n C_t = F.linear(x_branch, self.proj_C.weight, None)\n\n y = _mamba_ext.selective_scan_forward(x_branch, delta, B_t, C_t, self.A_log, self.D)\n z = F.silu(z)\n y = y * z\n y = F.linear(y, self.out_proj.weight, None)\n return y + residual\n" }
result ← submit_kernel (ok)
oksubmit_kernel submit_kernel PASSED: (5 / 5) correctness trials all passed. Kernel runtime: 3.08 μs Runtime stats: mean=3.08μs median=?μs std=0.01μs Numerical precision: max_abs_err=4.77e-07 mean_abs_err=4.52e-09 max_rel_err=1.54e-01 Memory: 554.22 MB peak (ref: 3690.75 MB, ratio: 0.1502x) Kernel launches: 59 (ref: 26754, fusion ratio: 453.4576) Energy: 1592.1 mJ/run (ref: 13055.16 mJ, ratio: 8.2x) SOL score: 1.0 Flagged for excessive speedup — this submission may have been rejected by automated review.
final submitted kernel (193 lines, 7,234 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 causal_conv_silu_forward(torch::Tensor x, torch::Tensor w, torch::Tensor bias);
torch::Tensor selective_scan_forward(torch::Tensor x, torch::Tensor delta, torch::Tensor Bt, torch::Tensor Ct, torch::Tensor A_log, torch::Tensor D);
"""

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

__global__ void causal_conv_silu_kernel(
    const float* __restrict__ x,
    const float* __restrict__ w,
    const float* __restrict__ bias,
    float* __restrict__ out,
    long sB, long sL, long sI,
    int B, int L, int I, int K)
{
    long total = (long)B * L * I;
    long idx = (long)blockIdx.x * blockDim.x + threadIdx.x;
    long stride = (long)blockDim.x * gridDim.x;
    for (; idx < total; idx += stride) {
        int i = (int)(idx % I);
        long tmp = idx / I;
        int t = (int)(tmp % L);
        int b = (int)(tmp / L);
        float acc = bias[i];
        // PyTorch Conv1d with padding K-1, returning the first L positions:
        // input index = t + k - (K-1).
        #pragma unroll
        for (int k = 0; k < 8; ++k) {
            if (k < K) {
                int ti = t + k - (K - 1);
                if (ti >= 0) {
                    acc += x[(long)b * sB + (long)ti * sL + (long)i * sI] * w[(long)i * K + k];
                }
            }
        }
        float sig = 1.0f / (1.0f + expf(-acc));
        out[idx] = acc * sig;
    }
}

__global__ void selective_scan_kernel(
    const float* __restrict__ x,
    const float* __restrict__ delta,
    const float* __restrict__ Bt,
    const float* __restrict__ Ct,
    const float* __restrict__ A_log,
    const float* __restrict__ D,
    float* __restrict__ y,
    int B, int L, int I, int N)
{
    int lane = threadIdx.x & 31;
    int warp_in_block = threadIdx.x >> 5;
    int warps_per_block = blockDim.x >> 5;
    int wi = blockIdx.x * warps_per_block + warp_in_block;
    int total_warps = B * I;
    if (wi >= total_warps) return;

    int b = wi / I;
    int i = wi - b * I;
    int n = lane;

    float h = 0.0f;
    float aval = 0.0f;
    if (n < N) {
        aval = -expf(A_log[(long)i * N + n]);
    }
    float dskip = D[i];

    for (int t = 0; t < L; ++t) {
        long xi = ((long)b * L + t) * I + i;
        float xv = x[xi];
        float del = delta[xi];
        float prod = 0.0f;
        if (n < N) {
            long bcn = ((long)b * L + t) * N + n;
            float da = expf(del * aval);
            h = da * h + (xv * del) * Bt[bcn];
            prod = h * Ct[bcn];
        }
        // warp reduction over the N=16 active lanes (remaining lanes hold zero)
        prod += __shfl_down_sync(0xffffffff, prod, 16);
        prod += __shfl_down_sync(0xffffffff, prod, 8);
        prod += __shfl_down_sync(0xffffffff, prod, 4);
        prod += __shfl_down_sync(0xffffffff, prod, 2);
        prod += __shfl_down_sync(0xffffffff, prod, 1);
        if (lane == 0) {
            y[xi] = prod + xv * dskip;
        }
    }
}

torch::Tensor causal_conv_silu_forward(torch::Tensor x, torch::Tensor w, torch::Tensor bias) {
    TORCH_CHECK(x.is_cuda() && w.is_cuda() && bias.is_cuda(), "tensors must be CUDA");
    TORCH_CHECK(x.scalar_type() == at::kFloat, "only float32 is supported");
    TORCH_CHECK(w.scalar_type() == at::kFloat && bias.scalar_type() == at::kFloat, "only float32 is supported");
    int B = (int)x.size(0);
    int L = (int)x.size(1);
    int I = (int)x.size(2);
    int K = (int)w.size(2);
    auto out = torch::empty({B, L, I}, x.options());
    long total = (long)B * L * I;
    int threads = 256;
    int blocks = (int)((total + threads - 1) / threads);
    if (blocks > 65535) blocks = 65535;
    causal_conv_silu_kernel<<<blocks, threads, 0, at::cuda::getCurrentCUDAStream()>>>(
        x.data_ptr<float>(), w.data_ptr<float>(), bias.data_ptr<float>(), out.data_ptr<float>(),
        x.stride(0), x.stride(1), x.stride(2), B, L, I, K);
    return out;
}

torch::Tensor selective_scan_forward(torch::Tensor x, torch::Tensor delta, torch::Tensor Bt, torch::Tensor Ct, torch::Tensor A_log, torch::Tensor D) {
    TORCH_CHECK(x.is_cuda() && delta.is_cuda() && Bt.is_cuda() && Ct.is_cuda() && A_log.is_cuda() && D.is_cuda(), "tensors must be CUDA");
    TORCH_CHECK(x.scalar_type() == at::kFloat, "only float32 is supported");
    int B = (int)x.size(0);
    int L = (int)x.size(1);
    int I = (int)x.size(2);
    int N = (int)Bt.size(2);
    auto y = torch::empty_like(x);
    int threads = 128; // 4 warps; each warp scans one (batch, channel)
    int warps_per_block = threads / 32;
    int blocks = (B * I + warps_per_block - 1) / warps_per_block;
    selective_scan_kernel<<<blocks, threads, 0, at::cuda::getCurrentCUDAStream()>>>(
        x.data_ptr<float>(), delta.data_ptr<float>(), Bt.data_ptr<float>(), Ct.data_ptr<float>(),
        A_log.data_ptr<float>(), D.data_ptr<float>(), y.data_ptr<float>(), B, L, I, N);
    return y;
}
"""

_mamba_ext = load_inline(
    name="mamba_block_custom_ext_v1",
    cpp_sources=_cpp_src,
    cuda_sources=_cuda_src,
    functions=["causal_conv_silu_forward", "selective_scan_forward"],
    extra_cflags=["-O3"],
    extra_cuda_cflags=["-O3"],
    verbose=False,
)

class ModelNew(nn.Module):
    def __init__(self, d_model: int, d_state: int, d_conv: int, expand: int):
        super(ModelNew, self).__init__()
        self.d_model = d_model
        self.d_inner = d_model * expand
        self.d_state = d_state
        self.d_conv = d_conv

        self.norm = nn.RMSNorm(d_model)
        self.in_proj = nn.Linear(d_model, self.d_inner * 2, bias=False)
        self.conv1d = nn.Conv1d(self.d_inner, self.d_inner, d_conv,
                                padding=d_conv - 1, groups=self.d_inner, bias=True)
        self.activation = nn.SiLU()

        self.A_log = nn.Parameter(torch.randn(self.d_inner, d_state))
        self.proj_B = nn.Linear(self.d_inner, d_state, bias=False)
        self.proj_C = nn.Linear(self.d_inner, d_state, bias=False)
        self.proj_delta = nn.Linear(self.d_inner, self.d_inner, bias=True)
        self.D = nn.Parameter(torch.ones(self.d_inner))

        self.out_proj = nn.Linear(self.d_inner, d_model, bias=False)

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        residual = x
        x_norm = self.norm(x)
        xz = F.linear(x_norm, self.in_proj.weight, None)
        I = self.d_inner
        x_branch = xz[..., :I]
        z = xz[..., I:]

        # Fused causal depthwise convolution over the x branch and SiLU activation.
        x_branch = _mamba_ext.causal_conv_silu_forward(x_branch, self.conv1d.weight, self.conv1d.bias)

        delta = F.softplus(F.linear(x_branch, self.proj_delta.weight, self.proj_delta.bias))
        B_t = F.linear(x_branch, self.proj_B.weight, None)
        C_t = F.linear(x_branch, self.proj_C.weight, None)

        y = _mamba_ext.selective_scan_forward(x_branch, delta, B_t, C_t, self.A_log, self.D)
        z = F.silu(z)
        y = y * z
        y = F.linear(y, self.out_proj.weight, None)
        return y + residual