gpt-5.5-priority · popcorn · l123_single_gpt
correct 3_SSM2D_FusedBidirectionalCrossScan.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
0.01 μs
ref runtime
0.19 μs
speedup
20.13x
started
2026-05-05T00:02:03Z
timingkernelbench.timing
kernel runtime
0.01 μs
ref runtime
0.19 μs
speedup
20.13x
kernel mean / median / std
0.01 / — / 0.02 μs
ref mean / median / std
0.19 / — / 0.06 μs
numerical precisioneval_kernel_against_ref
max abs error
1.91e-06
mean abs error
4.04e-08
max rel error
0.67
mean rel error
7.30e-07
gpu memoryextended_metrics.measure_memory
kernel peak
35.00 MB
ref peak
42.00 MB
kernel / ref
0.83x
kernel launches / fusiontorch.profiler
kernel launches
2
ref launches
114
fusion ratio (ref / kernel)
57.00x
total cuda time
60 μs
SOL (speed-of-light)nsight_hardware_counters
SOL score (0–1)
0.161
dram util %
6.3%
compute util %
16.1%
bottleneck
compute-bound
dominant pipe
FP32
arithmetic intensity
—
ridge point
20.00
roofline / occupancynsight_hardware_counters
dram bandwidth
211.28 GB/s
dram util %
6.3%
fp32 tflops
—
fp32 util %
—
fp16 tflops
—
occupancy %
69.6%
bottleneck
compute-bound
dominant pipe %
16.1%
arithmetic intensity
—
ridge point
20.00
L1 hit %
—
L2 hit %
71.9%
ld sectors / req
3.37
st sectors / req
—
regs / thread
32
smem / block
1024 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
276.1%
mio_throttle
234.3%
short_scoreboard
169.2%
flagssubmit_kernel guard
excessive speedup
flagged
reference PyTorch source (the prompt the agent sees)
import torch
import torch.nn as nn

# Default ModelNew is PyTorch-only. run_and_check: use check_kernel=False with backend=cuda
# until the candidate file contains a real CUDA kernel (see kernel_static_checker).


class Model(nn.Module):
    """
    Fused **2D bidirectional cross-scan** block for robotics **SSM-hybrid** backbones
    (e.g. Vision Mamba / VMamba-style encoders on overhead maps, costmaps, or
    multi-sensor tiles).

    One forward fuses four fixed scan permutations of ``(H, W)`` into 1D sequences,
    applies a **shared depthwise Conv1d** (lightweight stand-in for a selective SSM
    along the scan), maps each result back to the grid, **sums** the four
    directional contributions, and adds a **residual**.

    Scan order (same spirit as 2D cross-scan / S2D):
      LR — row-major left→right, rows top→bottom  
      RL — row-major right→left per row  
      TB — column-wise top→bottom, columns left→right  
      BT — flip vertical then same as TB  
    """

    def __init__(self, channels: int, kernel_size: int = 3):
        super().__init__()
        assert channels >= 1
        assert kernel_size % 2 == 1, "use odd kernel_size for same-length conv1d"
        self.channels = channels
        pad = kernel_size // 2
        self.dw_conv = nn.Conv1d(
            channels,
            channels,
            kernel_size,
            padding=pad,
            groups=channels,
            bias=True,
        )

    def _apply_scan_conv_unscan(self, x: torch.Tensor) -> torch.Tensor:
        """Shared depthwise conv along flattened 2D, then map back to (B,C,H,W)."""
        B, C, H, W = x.shape
        seq = x.flatten(2)
        y = self.dw_conv(seq)
        return y.view(B, C, H, W)

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        """
        Args:
            x: (B, C, H, W) spatial features (map / image / fused tile).
        Returns:
            (B, C, H, W) after fused four-way cross-scan + residual.
        """
        B, C, H, W = x.shape
        assert C == self.channels

        acc = torch.zeros_like(x)

        # LR: row-major
        acc = acc + self._apply_scan_conv_unscan(x)

        # RL: flip W, scan, unflip
        xr = x.flip(dims=(-1,))
        acc = acc + self._apply_scan_conv_unscan(xr).flip(dims=(-1,))

        # TB: columns as sequences (permute to W,H, flatten)
        xt = x.permute(0, 1, 3, 2).contiguous()
        Bt, Ct, Wt, Ht = xt.shape
        seq_t = xt.flatten(2)
        yt = self.dw_conv(seq_t).view(Bt, Ct, Wt, Ht).permute(0, 1, 3, 2).contiguous()
        acc = acc + yt

        # BT: flip H then same TB path
        xb = x.flip(dims=(-2,))
        xbt = xb.permute(0, 1, 3, 2).contiguous()
        Bb, Cb, Wb, Hb = xbt.shape
        seq_b = xbt.flatten(2)
        yb = self.dw_conv(seq_b).view(Bb, Cb, Wb, Hb).permute(0, 1, 3, 2).contiguous().flip(
            dims=(-2,)
        )
        acc = acc + yb

        return x + acc


class ModelNew(nn.Module):
    """KernelBench candidate entry point; default delegates to ``Model``."""

    def __init__(self, channels: int, kernel_size: int = 3):
        super().__init__()
        self._impl = Model(channels, kernel_size=kernel_size)

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        return self._impl(x)


batch_size = 4
channels = 64
height = 32
width = 32
kernel_size = 3


def get_inputs():
    p = popcorn_pri
    x = torch.randn(p.jitter_int(batch_size), channels, p.jitter_int(height), p.jitter_int(width))
    return [x]


def get_init_inputs():
    return [channels, kernel_size]
turn 0 · FINAL · 1 tool call(s) · 282.3s turn · 282.3s LLM · 9,049 chars (click to expand)
turn 0282.3s turn · 282.3s LLM · 0.0s tools · FINAL
call → submit_kernel
callsubmit_kernel { "kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\n\n_cpp_src = r'''\n#include <torch/extension.h>\n\ntorch::Tensor cross_scan_forward(torch::Tensor x, torch::Tensor weight, torch::Tensor bias);\n'''\n\n_cuda_src = r'''\n#include <torch/extension.h>\n#include <ATen/cuda/CUDAContext.h>\n#include <c10/cuda/CUDAStream.h>\n\n#define CHECK_CUDA(x) TORCH_CHECK((x).is_cuda(), #x \" must be a CUDA tensor\")\n#define CHECK_CONTIGUOUS(x) TORCH_CHECK((x).is_contiguous(), #x \" must be contiguous\")\n#define CHECK_FLOAT(x) TORCH_CHECK((x).scalar_type() == at::kFloat, #x \" must be float32\")\n\n__global__ void cross_scan_32_k3_kernel(\n const float* __restrict__ x,\n const float* __restrict__ weight,\n const float* __restrict__ bias,\n float* __restrict__ out,\n int BC,\n int C)\n{\n const int pix = blockIdx.x * blockDim.x + threadIdx.x; // 0..1023\n const int bc = blockIdx.y;\n if (pix >= 1024 || bc >= BC) return;\n\n const int c = bc % C;\n const int base = bc << 10; // *1024\n const int off = base + pix;\n\n const float w0 = __ldg(weight + c * 3 + 0);\n const float w1 = __ldg(weight + c * 3 + 1);\n const float w2 = __ldg(weight + c * 3 + 2);\n const float b = __ldg(bias + c);\n const float xc = __ldg(x + off);\n\n const int h = pix >> 5;\n const int w = pix & 31;\n\n float acc = 0.0f;\n\n // LR: original row-major sequence\n float v = b + w1 * xc;\n if (pix > 0) v += w0 * __ldg(x + base + pix - 1);\n if (pix < 1023) v += w2 * __ldg(x + base + pix + 1);\n acc += v;\n\n // RL: row-major sequence after horizontal flip, then unflip\n const int nR = (h << 5) + (31 - w);\n v = b + w1 * xc;\n if (nR > 0) {\n const int s = nR - 1;\n const int orig = (s & ~31) + (31 - (s & 31));\n v += w0 * __ldg(x + base + orig);\n }\n if (nR < 1023) {\n const int s = nR + 1;\n const int orig = (s & ~31) + (31 - (s & 31));\n v += w2 * __ldg(x + base + orig);\n }\n acc += v;\n\n // TB: column-major sequence (W,H), mapped back\n const int nT = (w << 5) + h;\n v = b + w1 * xc;\n if (nT > 0) {\n const int s = nT - 1;\n const int orig = ((s & 31) << 5) + (s >> 5);\n v += w0 * __ldg(x + base + orig);\n }\n if (nT < 1023) {\n const int s = nT + 1;\n const int orig = ((s & 31) << 5) + (s >> 5);\n v += w2 * __ldg(x + base + orig);\n }\n acc += v;\n\n // BT: column-major sequence after vertical flip, then unflip\n const int nB = (w << 5) + (31 - h);\n v = b + w1 * xc;\n if (nB > 0) {\n const int s = nB - 1;\n const int orig = ((31 - (s & 31)) << 5) + (s >> 5);\n v += w0 * __ldg(x + base + orig);\n }\n if (nB < 1023) {\n const int s = nB + 1;\n const int orig = ((31 - (s & 31)) << 5) + (s >> 5);\n v += w2 * __ldg(x + base + orig);\n }\n acc += v;\n\n out[off] = xc + acc;\n}\n\n__global__ void cross_scan_generic_kernel(\n const float* __restrict__ x,\n const float* __restrict__ weight,\n const float* __restrict__ bias,\n float* __restrict__ out,\n int total,\n int C,\n int H,\n int W,\n int K)\n{\n const int idx = blockIdx.x * blockDim.x + threadIdx.x;\n if (idx >= total) return;\n\n const int N = H * W;\n const int pix = idx % N;\n const int bc = idx / N;\n const int c = bc % C;\n const int base = bc * N;\n const int h = pix / W;\n const int w = pix - h * W;\n const int pad = K >> 1;\n const float* __restrict__ wt = weight + c * K;\n const float b = __ldg(bias + c);\n const float xc = __ldg(x + idx);\n\n float acc = 0.0f;\n\n // LR\n int n = pix;\n float v = b;\n for (int j = 0; j < K; ++j) {\n int s = n + j - pad;\n if ((unsigned)s < (unsigned)N) v += __ldg(wt + j) * __ldg(x + base + s);\n }\n acc += v;\n\n // RL\n n = h * W + (W - 1 - w);\n v = b;\n for (int j = 0; j < K; ++j) {\n int s = n + j - pad;\n if ((unsigned)s < (unsigned)N) {\n int hh = s / W;\n int ww = s - hh * W;\n int orig = hh * W + (W - 1 - ww);\n v += __ldg(wt + j) * __ldg(x + base + orig);\n }\n }\n acc += v;\n\n // TB\n n = w * H + h;\n v = b;\n for (int j = 0; j < K; ++j) {\n int s = n + j - pad;\n if ((unsigned)s < (unsigned)N) {\n int ww = s / H;\n int hh = s - ww * H;\n int orig = hh * W + ww;\n v += __ldg(wt + j) * __ldg(x + base + orig);\n }\n }\n acc += v;\n\n // BT\n n = w * H + (H - 1 - h);\n v = b;\n for (int j = 0; j < K; ++j) {\n int s = n + j - pad;\n if ((unsigned)s < (unsigned)N) {\n int ww = s / H;\n int hf = s - ww * H;\n int orig = (H - 1 - hf) * W + ww;\n v += __ldg(wt + j) * __ldg(x + base + orig);\n }\n }\n acc += v;\n\n out[idx] = xc + acc;\n}\n\ntorch::Tensor cross_scan_forward(torch::Tensor x, torch::Tensor weight, torch::Tensor bias) {\n CHECK_CUDA(x); CHECK_CUDA(weight); CHECK_CUDA(bias);\n CHECK_CONTIGUOUS(x); CHECK_CONTIGUOUS(weight); CHECK_CONTIGUOUS(bias);\n CHECK_FLOAT(x); CHECK_FLOAT(weight); CHECK_FLOAT(bias);\n\n TORCH_CHECK(x.dim() == 4, \"x must be B,C,H,W\");\n TORCH_CHECK(weight.dim() == 3 && weight.size(1) == 1, \"weight must be C,1,K\");\n TORCH_CHECK(bias.dim() == 1, \"bias must be C\");\n\n const int B = (int)x.size(0);\n const int C = (int)x.size(1);\n const int H = (int)x.size(2);\n const int W = (int)x.size(3);\n const int K = (int)weight.size(2);\n TORCH_CHECK(weight.size(0) == C && bias.size(0) == C, \"channel mismatch\");\n\n auto out = torch::empty_like(x);\n cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n\n if (H == 32 && W == 32 && K == 3) {\n const int BC = B * C;\n dim3 block(256);\n dim3 grid(4, BC);\n cross_scan_32_k3_kernel<<<grid, block, 0, stream>>>(\n x.data_ptr<float>(), weight.data_ptr<float>(), bias.data_ptr<float>(),\n out.data_ptr<float>(), BC, C);\n } else {\n const int total = B * C * H * W;\n const int block = 256;\n const int grid = (total + block - 1) / block;\n cross_scan_generic_kernel<<<grid, block, 0, stream>>>(\n x.data_ptr<float>(), weight.data_ptr<float>(), bias.data_ptr<float>(),\n out.data_ptr<float>(), total, C, H, W, K);\n }\n C10_CUDA_KERNEL_LAUNCH_CHECK();\n return out;\n}\n'''\n\n_ext = load_inline(\n name=\"cross_scan_depthwise_ext_v1\",\n cpp_sources=_cpp_src,\n cuda_sources=_cuda_src,\n functions=[\"cross_scan_forward\"],\n extra_cflags=[\"-O3\"],\n extra_cuda_cflags=[\"-O3\"],\n with_cuda=True,\n verbose=False,\n)\n\n\nclass ModelNew(nn.Module):\n def __init__(self, channels: int, kernel_size: int = 3):\n super().__init__()\n assert channels >= 1\n assert kernel_size % 2 == 1, \"use odd kernel_size for same-length conv1d\"\n self.channels = channels\n pad = kernel_size // 2\n self.dw_conv = nn.Conv1d(\n channels,\n channels,\n kernel_size,\n padding=pad,\n groups=channels,\n bias=True,\n )\n\n def forward(self, x: torch.Tensor) -> torch.Tensor:\n # Benchmark inputs are contiguous NCHW fp32 CUDA tensors.\n return _ext.cross_scan_forward(x, self.dw_conv.weight, self.dw_conv.bias)\n" }
result ← submit_kernel (ok)
oksubmit_kernel submit_kernel PASSED: (5 / 5) correctness trials all passed. Kernel runtime: 0.01 μs Runtime stats: mean=0.01μs median=?μs std=0.02μs Numerical precision: max_abs_err=1.91e-06 mean_abs_err=4.04e-08 max_rel_err=6.67e-01 Memory: 35.0 MB peak (ref: 42.0 MB, ratio: 0.8333x) Kernel launches: 2 (ref: 114, fusion ratio: 57.0) SOL score: 0.1608 Flagged for excessive speedup — this submission may have been rejected by automated review.
final submitted kernel (254 lines, 7,339 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 cross_scan_forward(torch::Tensor x, torch::Tensor weight, torch::Tensor bias);
'''

_cuda_src = r'''
#include <torch/extension.h>
#include <ATen/cuda/CUDAContext.h>
#include <c10/cuda/CUDAStream.h>

#define CHECK_CUDA(x) TORCH_CHECK((x).is_cuda(), #x " must be a CUDA tensor")
#define CHECK_CONTIGUOUS(x) TORCH_CHECK((x).is_contiguous(), #x " must be contiguous")
#define CHECK_FLOAT(x) TORCH_CHECK((x).scalar_type() == at::kFloat, #x " must be float32")

__global__ void cross_scan_32_k3_kernel(
    const float* __restrict__ x,
    const float* __restrict__ weight,
    const float* __restrict__ bias,
    float* __restrict__ out,
    int BC,
    int C)
{
    const int pix = blockIdx.x * blockDim.x + threadIdx.x;  // 0..1023
    const int bc = blockIdx.y;
    if (pix >= 1024 || bc >= BC) return;

    const int c = bc % C;
    const int base = bc << 10;  // *1024
    const int off = base + pix;

    const float w0 = __ldg(weight + c * 3 + 0);
    const float w1 = __ldg(weight + c * 3 + 1);
    const float w2 = __ldg(weight + c * 3 + 2);
    const float b  = __ldg(bias + c);
    const float xc = __ldg(x + off);

    const int h = pix >> 5;
    const int w = pix & 31;

    float acc = 0.0f;

    // LR: original row-major sequence
    float v = b + w1 * xc;
    if (pix > 0)    v += w0 * __ldg(x + base + pix - 1);
    if (pix < 1023) v += w2 * __ldg(x + base + pix + 1);
    acc += v;

    // RL: row-major sequence after horizontal flip, then unflip
    const int nR = (h << 5) + (31 - w);
    v = b + w1 * xc;
    if (nR > 0) {
        const int s = nR - 1;
        const int orig = (s & ~31) + (31 - (s & 31));
        v += w0 * __ldg(x + base + orig);
    }
    if (nR < 1023) {
        const int s = nR + 1;
        const int orig = (s & ~31) + (31 - (s & 31));
        v += w2 * __ldg(x + base + orig);
    }
    acc += v;

    // TB: column-major sequence (W,H), mapped back
    const int nT = (w << 5) + h;
    v = b + w1 * xc;
    if (nT > 0) {
        const int s = nT - 1;
        const int orig = ((s & 31) << 5) + (s >> 5);
        v += w0 * __ldg(x + base + orig);
    }
    if (nT < 1023) {
        const int s = nT + 1;
        const int orig = ((s & 31) << 5) + (s >> 5);
        v += w2 * __ldg(x + base + orig);
    }
    acc += v;

    // BT: column-major sequence after vertical flip, then unflip
    const int nB = (w << 5) + (31 - h);
    v = b + w1 * xc;
    if (nB > 0) {
        const int s = nB - 1;
        const int orig = ((31 - (s & 31)) << 5) + (s >> 5);
        v += w0 * __ldg(x + base + orig);
    }
    if (nB < 1023) {
        const int s = nB + 1;
        const int orig = ((31 - (s & 31)) << 5) + (s >> 5);
        v += w2 * __ldg(x + base + orig);
    }
    acc += v;

    out[off] = xc + acc;
}

__global__ void cross_scan_generic_kernel(
    const float* __restrict__ x,
    const float* __restrict__ weight,
    const float* __restrict__ bias,
    float* __restrict__ out,
    int total,
    int C,
    int H,
    int W,
    int K)
{
    const int idx = blockIdx.x * blockDim.x + threadIdx.x;
    if (idx >= total) return;

    const int N = H * W;
    const int pix = idx % N;
    const int bc = idx / N;
    const int c = bc % C;
    const int base = bc * N;
    const int h = pix / W;
    const int w = pix - h * W;
    const int pad = K >> 1;
    const float* __restrict__ wt = weight + c * K;
    const float b = __ldg(bias + c);
    const float xc = __ldg(x + idx);

    float acc = 0.0f;

    // LR
    int n = pix;
    float v = b;
    for (int j = 0; j < K; ++j) {
        int s = n + j - pad;
        if ((unsigned)s < (unsigned)N) v += __ldg(wt + j) * __ldg(x + base + s);
    }
    acc += v;

    // RL
    n = h * W + (W - 1 - w);
    v = b;
    for (int j = 0; j < K; ++j) {
        int s = n + j - pad;
        if ((unsigned)s < (unsigned)N) {
            int hh = s / W;
            int ww = s - hh * W;
            int orig = hh * W + (W - 1 - ww);
            v += __ldg(wt + j) * __ldg(x + base + orig);
        }
    }
    acc += v;

    // TB
    n = w * H + h;
    v = b;
    for (int j = 0; j < K; ++j) {
        int s = n + j - pad;
        if ((unsigned)s < (unsigned)N) {
            int ww = s / H;
            int hh = s - ww * H;
            int orig = hh * W + ww;
            v += __ldg(wt + j) * __ldg(x + base + orig);
        }
    }
    acc += v;

    // BT
    n = w * H + (H - 1 - h);
    v = b;
    for (int j = 0; j < K; ++j) {
        int s = n + j - pad;
        if ((unsigned)s < (unsigned)N) {
            int ww = s / H;
            int hf = s - ww * H;
            int orig = (H - 1 - hf) * W + ww;
            v += __ldg(wt + j) * __ldg(x + base + orig);
        }
    }
    acc += v;

    out[idx] = xc + acc;
}

torch::Tensor cross_scan_forward(torch::Tensor x, torch::Tensor weight, torch::Tensor bias) {
    CHECK_CUDA(x); CHECK_CUDA(weight); CHECK_CUDA(bias);
    CHECK_CONTIGUOUS(x); CHECK_CONTIGUOUS(weight); CHECK_CONTIGUOUS(bias);
    CHECK_FLOAT(x); CHECK_FLOAT(weight); CHECK_FLOAT(bias);

    TORCH_CHECK(x.dim() == 4, "x must be B,C,H,W");
    TORCH_CHECK(weight.dim() == 3 && weight.size(1) == 1, "weight must be C,1,K");
    TORCH_CHECK(bias.dim() == 1, "bias must be C");

    const int B = (int)x.size(0);
    const int C = (int)x.size(1);
    const int H = (int)x.size(2);
    const int W = (int)x.size(3);
    const int K = (int)weight.size(2);
    TORCH_CHECK(weight.size(0) == C && bias.size(0) == C, "channel mismatch");

    auto out = torch::empty_like(x);
    cudaStream_t stream = at::cuda::getCurrentCUDAStream();

    if (H == 32 && W == 32 && K == 3) {
        const int BC = B * C;
        dim3 block(256);
        dim3 grid(4, BC);
        cross_scan_32_k3_kernel<<<grid, block, 0, stream>>>(
            x.data_ptr<float>(), weight.data_ptr<float>(), bias.data_ptr<float>(),
            out.data_ptr<float>(), BC, C);
    } else {
        const int total = B * C * H * W;
        const int block = 256;
        const int grid = (total + block - 1) / block;
        cross_scan_generic_kernel<<<grid, block, 0, stream>>>(
            x.data_ptr<float>(), weight.data_ptr<float>(), bias.data_ptr<float>(),
            out.data_ptr<float>(), total, C, H, W, K);
    }
    C10_CUDA_KERNEL_LAUNCH_CHECK();
    return out;
}
'''

_ext = load_inline(
    name="cross_scan_depthwise_ext_v1",
    cpp_sources=_cpp_src,
    cuda_sources=_cuda_src,
    functions=["cross_scan_forward"],
    extra_cflags=["-O3"],
    extra_cuda_cflags=["-O3"],
    with_cuda=True,
    verbose=False,
)


class ModelNew(nn.Module):
    def __init__(self, channels: int, kernel_size: int = 3):
        super().__init__()
        assert channels >= 1
        assert kernel_size % 2 == 1, "use odd kernel_size for same-length conv1d"
        self.channels = channels
        pad = kernel_size // 2
        self.dw_conv = nn.Conv1d(
            channels,
            channels,
            kernel_size,
            padding=pad,
            groups=channels,
            bias=True,
        )

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        # Benchmark inputs are contiguous NCHW fp32 CUDA tensors.
        return _ext.cross_scan_forward(x, self.dw_conv.weight, self.dw_conv.bias)