gpt-5.5-priority · original · l123_single_gpt
correct 82_conv_depthwise_2D_square_input_square_kernel.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
1.07 μs
ref runtime
2.54 μs
speedup
2.37x
started
2026-05-05T00:31:59Z
timingkernelbench.timing
kernel runtime
1.07 μs
ref runtime
2.54 μs
speedup
2.37x
kernel mean / median / std
1.07 / — / 0.03 μs
ref mean / median / std
2.54 / — / 0.07 μs
numerical precisioneval_kernel_against_ref
max abs error
0.00
mean abs error
0.00
max rel error
0.00
mean rel error
0.00
gpu memoryextended_metrics.measure_memory
kernel peak
2073.02 MB
ref peak
2073.02 MB
kernel / ref
1.00x
kernel launches / fusiontorch.profiler
kernel launches
2
ref launches
6
fusion ratio (ref / kernel)
3.00x
total cuda time
30 μs
SOL (speed-of-light)nsight_hardware_counters
SOL score (0–1)
0.598
dram util %
59.8%
compute util %
14.6%
bottleneck
memory-bound
dominant pipe
FP32
arithmetic intensity
—
ridge point
20.00
roofline / occupancynsight_hardware_counters
dram bandwidth
2002.17 GB/s
dram util %
59.8%
fp32 tflops
—
fp32 util %
—
fp16 tflops
—
occupancy %
97.5%
bottleneck
memory-bound
dominant pipe %
14.6%
arithmetic intensity
—
ridge point
20.00
L1 hit %
—
L2 hit %
56.0%
ld sectors / req
3.99
st sectors / req
—
regs / thread
30
smem / block
21632 B
block size
512
peak bw
3350.00 GB/s
peak fp32 tflops
67.00
peak fp16 tflops
1979.00
warp stalls (top 5)nsight
long_scoreboard
1927.9%
short_scoreboard
136.4%
mio_throttle
59.9%
reference PyTorch source (the prompt the agent sees)
import torch
import torch.nn as nn

class Model(nn.Module):
    """
    Performs a depthwise 2D convolution operation with square input and square kernel.

    Args:
        in_channels (int): Number of channels in the input tensor.
        kernel_size (int): Size of the convolution kernel.
        stride (int, optional): Stride of the convolution. Defaults to 1.
        padding (int, optional): Padding applied to the input. Defaults to 0.
        bias (bool, optional): If `True`, adds a learnable bias to the output. Defaults to `False`.
    """
    def __init__(self, in_channels: int, kernel_size: int, stride: int = 1, padding: int = 0, bias: bool = False):
        super(Model, self).__init__()
        self.conv2d = nn.Conv2d(in_channels, in_channels, kernel_size, stride=stride, padding=padding, groups=in_channels, bias=bias)
        
    def forward(self, x: torch.Tensor) -> torch.Tensor:
        """
        Performs the depthwise 2D convolution.

        Args:
            x (torch.Tensor): Input tensor of shape (batch_size, in_channels, height, width).

        Returns:
            torch.Tensor: Output tensor of shape (batch_size, in_channels, height_out, width_out).
        """
        return self.conv2d(x)

# Test code
batch_size = 16
in_channels = 64
kernel_size = 3
width = 512
height = 512
stride = 1
padding = 0

def get_inputs():
    x = torch.rand(batch_size, in_channels, height, width)
    return [x]

def get_init_inputs():
    return [in_channels, kernel_size, stride, padding]
turn 0 · FINAL · 1 tool call(s) · 81.9s turn · 81.9s LLM · 7,912 chars (click to expand)
turn 081.9s turn · 81.9s 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_cpp_src = r\"\"\"\n#include <torch/extension.h>\n\ntorch::Tensor depthwise_conv_forward(torch::Tensor x, torch::Tensor weight, torch::Tensor bias, int64_t stride, int64_t padding);\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\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\nstatic constexpr int TILE_H = 8;\n\n__global__ void dw3x3_s1p0_tiled_kernel(\n const float* __restrict__ x,\n const float* __restrict__ w,\n const float* __restrict__ b,\n float* __restrict__ y,\n int N, int C, int H, int W, int Hout, int Wout, int has_bias)\n{\n extern __shared__ float sh[];\n float* sw = sh + (TILE_H + 2) * W;\n\n const int tx = threadIdx.x; // 0..63\n const int ty = threadIdx.y; // 0..7\n const int z = blockIdx.z;\n const int c = z % C;\n const int n = z / C;\n const int h0 = blockIdx.y * TILE_H;\n const int tile_rows = min(TILE_H, Hout - h0);\n const int load_rows = tile_rows + 2;\n\n const float* xbase = x + ((n * C + c) * H + h0) * W;\n const float* wbase = w + c * 9;\n\n const int tid = ty * blockDim.x + tx;\n if (tid < 9) sw[tid] = wbase[tid];\n\n // Load the input tile: (tile_rows+2) x W. This avoids the 9x rereads\n // that a direct depthwise implementation would perform.\n for (int rr = ty; rr < load_rows; rr += blockDim.y) {\n const float* src = xbase + rr * W;\n float* dst = sh + rr * W;\n for (int col = tx; col < W; col += blockDim.x) {\n dst[col] = src[col];\n }\n }\n __syncthreads();\n\n if (ty < tile_rows) {\n const float w0 = sw[0], w1 = sw[1], w2 = sw[2];\n const float w3 = sw[3], w4 = sw[4], w5 = sw[5];\n const float w6 = sw[6], w7 = sw[7], w8 = sw[8];\n const float bv = has_bias ? b[c] : 0.0f;\n float* yrow = y + ((n * C + c) * Hout + (h0 + ty)) * Wout;\n\n for (int ow = tx; ow < Wout; ow += blockDim.x) {\n const float* p = sh + ty * W + ow;\n float acc = bv;\n acc = fmaf(p[0], w0, acc);\n acc = fmaf(p[1], w1, acc);\n acc = fmaf(p[2], w2, acc);\n acc = fmaf(p[W], w3, acc);\n acc = fmaf(p[W + 1], w4, acc);\n acc = fmaf(p[W + 2], w5, acc);\n acc = fmaf(p[2 * W], w6, acc);\n acc = fmaf(p[2 * W+1], w7, acc);\n acc = fmaf(p[2 * W+2], w8, acc);\n yrow[ow] = acc;\n }\n }\n}\n\n__global__ void depthwise_generic_kernel(\n const float* __restrict__ x,\n const float* __restrict__ w,\n const float* __restrict__ b,\n float* __restrict__ y,\n long long total, int C, int H, int W, int K, int stride, int pad, int Hout, int Wout, int has_bias)\n{\n long long idx = (long long)blockIdx.x * blockDim.x + threadIdx.x;\n if (idx >= total) return;\n\n int ow = (int)(idx % Wout);\n long long t = idx / Wout;\n int oh = (int)(t % Hout);\n t /= Hout;\n int c = (int)(t % C);\n int n = (int)(t / C);\n\n const float* xnc = x + ((n * C + c) * H) * W;\n const float* wc = w + c * K * K;\n float acc = has_bias ? b[c] : 0.0f;\n\n int ih0 = oh * stride - pad;\n int iw0 = ow * stride - pad;\n for (int kh = 0; kh < K; ++kh) {\n int ih = ih0 + kh;\n if ((unsigned)ih >= (unsigned)H) continue;\n for (int kw = 0; kw < K; ++kw) {\n int iw = iw0 + kw;\n if ((unsigned)iw < (unsigned)W) {\n acc = fmaf(xnc[ih * W + iw], wc[kh * K + kw], acc);\n }\n }\n }\n y[idx] = acc;\n}\n\ntorch::Tensor depthwise_conv_forward(torch::Tensor x, torch::Tensor weight, torch::Tensor bias, int64_t stride64, int64_t padding64) {\n CHECK_CUDA(x);\n CHECK_CUDA(weight);\n CHECK_CONTIGUOUS(x);\n CHECK_CONTIGUOUS(weight);\n TORCH_CHECK(x.scalar_type() == at::kFloat, \"only float32 is supported\");\n TORCH_CHECK(weight.scalar_type() == at::kFloat, \"only float32 weights are supported\");\n\n const int N = (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 const int stride = (int)stride64;\n const int pad = (int)padding64;\n const int Hout = (H + 2 * pad - K) / stride + 1;\n const int Wout = (W + 2 * pad - K) / stride + 1;\n\n auto y = torch::empty({N, C, Hout, Wout}, x.options());\n const int has_bias = (bias.defined() && bias.numel() > 0) ? 1 : 0;\n const float* bptr = has_bias ? bias.data_ptr<float>() : nullptr;\n cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n\n if (K == 3 && stride == 1 && pad == 0 && W > 0 && Wout > 0) {\n dim3 block(64, TILE_H, 1);\n dim3 grid(1, (Hout + TILE_H - 1) / TILE_H, N * C);\n size_t smem = ((TILE_H + 2) * (size_t)W + 9) * sizeof(float);\n dw3x3_s1p0_tiled_kernel<<<grid, block, smem, stream>>>(\n x.data_ptr<float>(), weight.data_ptr<float>(), bptr, y.data_ptr<float>(),\n N, C, H, W, Hout, Wout, has_bias);\n } else {\n long long total = (long long)N * C * Hout * Wout;\n int threads = 256;\n int blocks = (int)((total + threads - 1) / threads);\n depthwise_generic_kernel<<<blocks, threads, 0, stream>>>(\n x.data_ptr<float>(), weight.data_ptr<float>(), bptr, y.data_ptr<float>(),\n total, C, H, W, K, stride, pad, Hout, Wout, has_bias);\n }\n return y;\n}\n\"\"\"\n\n_ext = load_inline(\n name=\"depthwise_conv2d_ext_v1\",\n cpp_sources=_cpp_src,\n cuda_sources=_cuda_src,\n functions=[\"depthwise_conv_forward\"],\n extra_cflags=[\"-O3\"],\n extra_cuda_cflags=[\"-O3\", \"--use_fast_math\"],\n verbose=False,\n)\n\nclass ModelNew(nn.Module):\n def __init__(self, in_channels: int, kernel_size: int, stride: int = 1, padding: int = 0, bias: bool = False):\n super(ModelNew, self).__init__()\n self.conv2d = nn.Conv2d(in_channels, in_channels, kernel_size, stride=stride, padding=padding, groups=in_channels, bias=bias)\n self.stride = stride\n self.padding = padding\n\n def forward(self, x: torch.Tensor) -> torch.Tensor:\n bias = self.conv2d.bias\n if bias is None:\n bias = torch.empty(0, device=x.device, dtype=x.dtype)\n return _ext.depthwise_conv_forward(x, self.conv2d.weight, bias, int(self.stride), int(self.padding))\n" }
result ← submit_kernel (ok)
oksubmit_kernel submit_kernel PASSED: (5 / 5) correctness trials all passed. Kernel runtime: 1.07 μs Runtime stats: mean=1.07μs median=?μs std=0.03μs Numerical precision: max_abs_err=0.00e+00 mean_abs_err=0.00e+00 max_rel_err=0.00e+00 Memory: 2073.02 MB peak (ref: 2073.02 MB, ratio: 1.0x) Kernel launches: 2 (ref: 6, fusion ratio: 3.0) SOL score: 0.5977
final submitted kernel (182 lines, 6,525 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 depthwise_conv_forward(torch::Tensor x, torch::Tensor weight, torch::Tensor bias, int64_t stride, int64_t padding);
"""

_cuda_src = r"""
#include <torch/extension.h>
#include <ATen/cuda/CUDAContext.h>
#include <cuda.h>
#include <cuda_runtime.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")

static constexpr int TILE_H = 8;

__global__ void dw3x3_s1p0_tiled_kernel(
    const float* __restrict__ x,
    const float* __restrict__ w,
    const float* __restrict__ b,
    float* __restrict__ y,
    int N, int C, int H, int W, int Hout, int Wout, int has_bias)
{
    extern __shared__ float sh[];
    float* sw = sh + (TILE_H + 2) * W;

    const int tx = threadIdx.x;       // 0..63
    const int ty = threadIdx.y;       // 0..7
    const int z = blockIdx.z;
    const int c = z % C;
    const int n = z / C;
    const int h0 = blockIdx.y * TILE_H;
    const int tile_rows = min(TILE_H, Hout - h0);
    const int load_rows = tile_rows + 2;

    const float* xbase = x + ((n * C + c) * H + h0) * W;
    const float* wbase = w + c * 9;

    const int tid = ty * blockDim.x + tx;
    if (tid < 9) sw[tid] = wbase[tid];

    // Load the input tile: (tile_rows+2) x W.  This avoids the 9x rereads
    // that a direct depthwise implementation would perform.
    for (int rr = ty; rr < load_rows; rr += blockDim.y) {
        const float* src = xbase + rr * W;
        float* dst = sh + rr * W;
        for (int col = tx; col < W; col += blockDim.x) {
            dst[col] = src[col];
        }
    }
    __syncthreads();

    if (ty < tile_rows) {
        const float w0 = sw[0], w1 = sw[1], w2 = sw[2];
        const float w3 = sw[3], w4 = sw[4], w5 = sw[5];
        const float w6 = sw[6], w7 = sw[7], w8 = sw[8];
        const float bv = has_bias ? b[c] : 0.0f;
        float* yrow = y + ((n * C + c) * Hout + (h0 + ty)) * Wout;

        for (int ow = tx; ow < Wout; ow += blockDim.x) {
            const float* p = sh + ty * W + ow;
            float acc = bv;
            acc = fmaf(p[0],       w0, acc);
            acc = fmaf(p[1],       w1, acc);
            acc = fmaf(p[2],       w2, acc);
            acc = fmaf(p[W],       w3, acc);
            acc = fmaf(p[W + 1],   w4, acc);
            acc = fmaf(p[W + 2],   w5, acc);
            acc = fmaf(p[2 * W],   w6, acc);
            acc = fmaf(p[2 * W+1], w7, acc);
            acc = fmaf(p[2 * W+2], w8, acc);
            yrow[ow] = acc;
        }
    }
}

__global__ void depthwise_generic_kernel(
    const float* __restrict__ x,
    const float* __restrict__ w,
    const float* __restrict__ b,
    float* __restrict__ y,
    long long total, int C, int H, int W, int K, int stride, int pad, int Hout, int Wout, int has_bias)
{
    long long idx = (long long)blockIdx.x * blockDim.x + threadIdx.x;
    if (idx >= total) return;

    int ow = (int)(idx % Wout);
    long long t = idx / Wout;
    int oh = (int)(t % Hout);
    t /= Hout;
    int c = (int)(t % C);
    int n = (int)(t / C);

    const float* xnc = x + ((n * C + c) * H) * W;
    const float* wc = w + c * K * K;
    float acc = has_bias ? b[c] : 0.0f;

    int ih0 = oh * stride - pad;
    int iw0 = ow * stride - pad;
    for (int kh = 0; kh < K; ++kh) {
        int ih = ih0 + kh;
        if ((unsigned)ih >= (unsigned)H) continue;
        for (int kw = 0; kw < K; ++kw) {
            int iw = iw0 + kw;
            if ((unsigned)iw < (unsigned)W) {
                acc = fmaf(xnc[ih * W + iw], wc[kh * K + kw], acc);
            }
        }
    }
    y[idx] = acc;
}

torch::Tensor depthwise_conv_forward(torch::Tensor x, torch::Tensor weight, torch::Tensor bias, int64_t stride64, int64_t padding64) {
    CHECK_CUDA(x);
    CHECK_CUDA(weight);
    CHECK_CONTIGUOUS(x);
    CHECK_CONTIGUOUS(weight);
    TORCH_CHECK(x.scalar_type() == at::kFloat, "only float32 is supported");
    TORCH_CHECK(weight.scalar_type() == at::kFloat, "only float32 weights are supported");

    const int N = (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);
    const int stride = (int)stride64;
    const int pad = (int)padding64;
    const int Hout = (H + 2 * pad - K) / stride + 1;
    const int Wout = (W + 2 * pad - K) / stride + 1;

    auto y = torch::empty({N, C, Hout, Wout}, x.options());
    const int has_bias = (bias.defined() && bias.numel() > 0) ? 1 : 0;
    const float* bptr = has_bias ? bias.data_ptr<float>() : nullptr;
    cudaStream_t stream = at::cuda::getCurrentCUDAStream();

    if (K == 3 && stride == 1 && pad == 0 && W > 0 && Wout > 0) {
        dim3 block(64, TILE_H, 1);
        dim3 grid(1, (Hout + TILE_H - 1) / TILE_H, N * C);
        size_t smem = ((TILE_H + 2) * (size_t)W + 9) * sizeof(float);
        dw3x3_s1p0_tiled_kernel<<<grid, block, smem, stream>>>(
            x.data_ptr<float>(), weight.data_ptr<float>(), bptr, y.data_ptr<float>(),
            N, C, H, W, Hout, Wout, has_bias);
    } else {
        long long total = (long long)N * C * Hout * Wout;
        int threads = 256;
        int blocks = (int)((total + threads - 1) / threads);
        depthwise_generic_kernel<<<blocks, threads, 0, stream>>>(
            x.data_ptr<float>(), weight.data_ptr<float>(), bptr, y.data_ptr<float>(),
            total, C, H, W, K, stride, pad, Hout, Wout, has_bias);
    }
    return y;
}
"""

_ext = load_inline(
    name="depthwise_conv2d_ext_v1",
    cpp_sources=_cpp_src,
    cuda_sources=_cuda_src,
    functions=["depthwise_conv_forward"],
    extra_cflags=["-O3"],
    extra_cuda_cflags=["-O3", "--use_fast_math"],
    verbose=False,
)

class ModelNew(nn.Module):
    def __init__(self, in_channels: int, kernel_size: int, stride: int = 1, padding: int = 0, bias: bool = False):
        super(ModelNew, self).__init__()
        self.conv2d = nn.Conv2d(in_channels, in_channels, kernel_size, stride=stride, padding=padding, groups=in_channels, bias=bias)
        self.stride = stride
        self.padding = padding

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        bias = self.conv2d.bias
        if bias is None:
            bias = torch.empty(0, device=x.device, dtype=x.dtype)
        return _ext.depthwise_conv_forward(x, self.conv2d.weight, bias, int(self.stride), int(self.padding))