gpt-5.5-priority · original · l123_single_gpt
correct 85_Conv2d_GroupNorm_Scale_MaxPool_Clamp.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.56 μs
ref runtime
2.52 μs
speedup
1.62x
started
2026-05-05T00:03:47Z
timingkernelbench.timing
kernel runtime
1.56 μs
ref runtime
2.52 μs
speedup
1.62x
kernel mean / median / std
1.56 / — / 0.01 μs
ref mean / median / std
2.52 / — / 0.00 μs
numerical precisioneval_kernel_against_ref
max abs error
5.96e-07
mean abs error
1.25e-08
max rel error
3.78
mean rel error
2.29e-07
gpu memoryextended_metrics.measure_memory
kernel peak
622.21 MB
ref peak
1088.37 MB
kernel / ref
0.57x
kernel launches / fusiontorch.profiler
kernel launches
9
ref launches
23
fusion ratio (ref / kernel)
2.56x
total cuda time
5091 μs
energyNVML (pynvml)
kernel mJ / run
240.14
ref mJ / run
240.46
ratio (ref / kernel)
1.00x
avg power
154.90 W
ref avg power
95.35 W
SOL (speed-of-light)nsight_hardware_counters
SOL score (0–1)
1.000
dram util %
100.0%
compute util %
5.3%
bottleneck
memory-bound
dominant pipe
tensor-core
arithmetic intensity
—
ridge point
295.22
roofline / occupancynsight_hardware_counters
dram bandwidth
8230.91 GB/s
dram util %
100.0%
fp32 tflops
—
fp32 util %
—
fp16 tflops
—
occupancy %
281.5%
bottleneck
memory-bound
dominant pipe %
5.3%
arithmetic intensity
—
ridge point
295.22
L1 hit %
—
L2 hit %
153.1%
ld sectors / req
24.40
st sectors / req
—
regs / thread
321
smem / block
39424 B
block size
768
peak bw
3350.00 GB/s
peak fp32 tflops
67.00
peak fp16 tflops
1979.00
warp stalls (top 5)nsight
long_scoreboard
12922.3%
mio_throttle
308.8%
short_scoreboard
243.8%
reference PyTorch source (the prompt the agent sees)
import torch
import torch.nn as nn

class Model(nn.Module):
    """
    Model that performs convolution, group normalization, scaling, max pooling, and clamping.
    """
    def __init__(self, in_channels, out_channels, kernel_size, num_groups, scale_shape, maxpool_kernel_size, clamp_min, clamp_max):
        super(Model, self).__init__()
        self.conv = nn.Conv2d(in_channels, out_channels, kernel_size)
        self.group_norm = nn.GroupNorm(num_groups, out_channels)
        self.scale = nn.Parameter(torch.ones(scale_shape))
        self.maxpool = nn.MaxPool2d(kernel_size=maxpool_kernel_size)
        self.clamp_min = clamp_min
        self.clamp_max = clamp_max

    def forward(self, x):
        """
        Args:
            x: Input tensor of shape (batch_size, in_channels, height, width).
        Returns:
            Output tensor of shape (batch_size, out_channels, height', width').
        """
        x = self.conv(x)
        x = self.group_norm(x)
        x = x * self.scale
        x = self.maxpool(x)
        x = torch.clamp(x, self.clamp_min, self.clamp_max)
        return x

batch_size = 128
in_channels = 8
out_channels = 64
height, width = 128, 128 
kernel_size = 3
num_groups = 16
scale_shape = (out_channels, 1, 1)
maxpool_kernel_size = 4
clamp_min = 0.0
clamp_max = 1.0

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

def get_init_inputs():
    return [in_channels, out_channels, kernel_size, num_groups, scale_shape, maxpool_kernel_size, clamp_min, clamp_max]
turn 0 · FINAL · 1 tool call(s) · 178.0s turn · 178.0s LLM · 9,272 chars (click to expand)
turn 0178.0s turn · 178.0s LLM · 0.0s tools · FINAL
reasoning
**Considering extension implementation** I need to implement an extension, but I'm wondering if custom convolution fusion might be too complex. I'm thinking that I could possibly use the returns from torch.conv2d instead. It feels like there’s a lot to figure out here, and I want to make sure I'm approaching this the right way. Do I have everything I need for this? I feel like I could benefit from more clarity on the implementation steps 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_cpp_src = r'''\n#include <torch/extension.h>\n#include <vector>\n\nvoid gn_stats_cuda(torch::Tensor x, torch::Tensor stats, int64_t groups);\nvoid gn_pool_out_cuda(torch::Tensor x, torch::Tensor stats, torch::Tensor gamma, torch::Tensor beta,\n torch::Tensor scale, torch::Tensor out, int64_t groups, int64_t pool,\n double clamp_min, double clamp_max);\n\ntorch::Tensor gn_pool_forward(torch::Tensor x, torch::Tensor gamma, torch::Tensor beta,\n torch::Tensor scale, int64_t groups, int64_t pool,\n double clamp_min, double clamp_max) {\n TORCH_CHECK(x.is_cuda(), \"x must be CUDA\");\n TORCH_CHECK(x.scalar_type() == at::kFloat, \"only float32 supported\");\n TORCH_CHECK(x.is_contiguous(), \"x must be contiguous\");\n TORCH_CHECK(gamma.is_cuda() && beta.is_cuda() && scale.is_cuda(), \"params must be CUDA\");\n const int64_t N = x.size(0);\n const int64_t C = x.size(1);\n const int64_t H = x.size(2);\n const int64_t W = x.size(3);\n const int64_t OH = (H - pool) / pool + 1;\n const int64_t OW = (W - pool) / pool + 1;\n auto stats = torch::empty({N, groups, 2}, x.options());\n auto out = torch::empty({N, C, OH, OW}, x.options());\n gn_stats_cuda(x, stats, groups);\n gn_pool_out_cuda(x, stats, gamma.contiguous(), beta.contiguous(), scale.contiguous(), out,\n groups, pool, clamp_min, clamp_max);\n return out;\n}\n'''\n\n_cuda_src = r'''\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <float.h>\n\n__global__ void gn_stats_kernel(const float* __restrict__ x, float* __restrict__ stats,\n int N, int C, int H, int W, int G) {\n const int bg = blockIdx.x;\n const int n = bg / G;\n const int g = bg - n * G;\n const int tid = threadIdx.x;\n const int HW = H * W;\n const int CG = C / G;\n const int total = CG * HW;\n float sum = 0.0f;\n float sum2 = 0.0f;\n const int c0 = g * CG;\n const int baseN = n * C * HW;\n\n for (int i = tid; i < total; i += blockDim.x) {\n int cg = i / HW;\n int hw = i - cg * HW;\n float v = x[baseN + (c0 + cg) * HW + hw];\n sum += v;\n sum2 += v * v;\n }\n\n __shared__ float ssum[256];\n __shared__ float ssq[256];\n ssum[tid] = sum;\n ssq[tid] = sum2;\n __syncthreads();\n\n for (int s = blockDim.x >> 1; s > 0; s >>= 1) {\n if (tid < s) {\n ssum[tid] += ssum[tid + s];\n ssq[tid] += ssq[tid + s];\n }\n __syncthreads();\n }\n if (tid == 0) {\n float inv_count = 1.0f / (float)total;\n float mean = ssum[0] * inv_count;\n float var = ssq[0] * inv_count - mean * mean;\n var = var < 0.0f ? 0.0f : var;\n float invstd = rsqrtf(var + 1.0e-5f);\n int off = bg * 2;\n stats[off] = mean;\n stats[off + 1] = invstd;\n }\n}\n\n__global__ void gn_pool_out_kernel(const float* __restrict__ x, const float* __restrict__ stats,\n const float* __restrict__ gamma, const float* __restrict__ beta,\n const float* __restrict__ scale, float* __restrict__ out,\n int N, int C, int H, int W, int G, int pool,\n int OH, int OW, float clamp_min, float clamp_max) {\n int64_t idx = (int64_t)blockIdx.x * blockDim.x + threadIdx.x;\n const int64_t total = (int64_t)N * C * OH * OW;\n if (idx >= total) return;\n\n int ow = idx % OW;\n int64_t t = idx / OW;\n int oh = t % OH;\n t /= OH;\n int c = t % C;\n int n = t / C;\n\n int CG = C / G;\n int g = c / CG;\n int stat_off = (n * G + g) * 2;\n float mean = stats[stat_off];\n float invstd = stats[stat_off + 1];\n float gm = gamma[c];\n float bt = beta[c];\n float sc = scale[c];\n int HW = H * W;\n int base = (n * C + c) * HW + (oh * pool) * W + (ow * pool);\n\n float m = -FLT_MAX;\n #pragma unroll\n for (int ph = 0; ph < 4; ++ph) {\n #pragma unroll\n for (int pw = 0; pw < 4; ++pw) {\n float v = x[base + ph * W + pw];\n v = ((v - mean) * invstd * gm + bt) * sc;\n m = fmaxf(m, v);\n }\n }\n // Correct for non-4 pool sizes if ever used with same API.\n if (pool != 4) {\n m = -FLT_MAX;\n for (int ph = 0; ph < pool; ++ph) {\n for (int pw = 0; pw < pool; ++pw) {\n float v = x[base + ph * W + pw];\n v = ((v - mean) * invstd * gm + bt) * sc;\n m = fmaxf(m, v);\n }\n }\n }\n m = fminf(fmaxf(m, clamp_min), clamp_max);\n out[idx] = m;\n}\n\nvoid gn_stats_cuda(torch::Tensor x, torch::Tensor stats, int64_t groups) {\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 dim3 blocks(N * (int)groups);\n const dim3 threads(256);\n gn_stats_kernel<<<blocks, threads>>>(x.data_ptr<float>(), stats.data_ptr<float>(), N, C, H, W, (int)groups);\n}\n\nvoid gn_pool_out_cuda(torch::Tensor x, torch::Tensor stats, torch::Tensor gamma, torch::Tensor beta,\n torch::Tensor scale, torch::Tensor out, int64_t groups, int64_t pool,\n double clamp_min, double clamp_max) {\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 OH = (int)out.size(2);\n const int OW = (int)out.size(3);\n const int64_t total = (int64_t)N * C * OH * OW;\n const int threads = 256;\n const int blocks = (int)((total + threads - 1) / threads);\n gn_pool_out_kernel<<<blocks, threads>>>(x.data_ptr<float>(), stats.data_ptr<float>(),\n gamma.data_ptr<float>(), beta.data_ptr<float>(), scale.data_ptr<float>(), out.data_ptr<float>(),\n N, C, H, W, (int)groups, (int)pool, OH, OW, (float)clamp_min, (float)clamp_max);\n}\n'''\n\n_ext = load_inline(\n name=\"fused_gn_pool_clamp_ext_v1\",\n cpp_sources=[_cpp_src],\n cuda_sources=[_cuda_src],\n functions=[\"gn_pool_forward\"],\n extra_cflags=[\"-O3\"],\n extra_cuda_cflags=[\"-O3\"],\n with_cuda=True,\n verbose=False,\n)\n\nclass ModelNew(nn.Module):\n def __init__(self, in_channels, out_channels, kernel_size, num_groups, scale_shape,\n maxpool_kernel_size, clamp_min, clamp_max):\n super(ModelNew, self).__init__()\n self.conv = nn.Conv2d(in_channels, out_channels, kernel_size)\n self.group_norm = nn.GroupNorm(num_groups, out_channels)\n self.scale = nn.Parameter(torch.ones(scale_shape))\n self.maxpool_kernel_size = maxpool_kernel_size\n self.clamp_min = float(clamp_min)\n self.clamp_max = float(clamp_max)\n\n def forward(self, x):\n y = F.conv2d(x, self.conv.weight, self.conv.bias, stride=1, padding=0, dilation=1, groups=1)\n y = y.contiguous()\n return _ext.gn_pool_forward(y, self.group_norm.weight, self.group_norm.bias,\n self.scale, self.group_norm.num_groups,\n int(self.maxpool_kernel_size), self.clamp_min, self.clamp_max)\n" }
result ← submit_kernel (ok)
oksubmit_kernel submit_kernel PASSED: (5 / 5) correctness trials all passed. Kernel runtime: 1.56 μs Runtime stats: mean=1.56μs median=?μs std=0.01μs Numerical precision: max_abs_err=5.96e-07 mean_abs_err=1.25e-08 max_rel_err=3.78e+00 Memory: 622.21 MB peak (ref: 1088.37 MB, ratio: 0.5717x) Kernel launches: 9 (ref: 23, fusion ratio: 2.5556) Energy: 240.14 mJ/run (ref: 240.46 mJ, ratio: 1.0013x) SOL score: 1.0
final submitted kernel (198 lines, 7,296 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>
#include <vector>

void gn_stats_cuda(torch::Tensor x, torch::Tensor stats, int64_t groups);
void gn_pool_out_cuda(torch::Tensor x, torch::Tensor stats, torch::Tensor gamma, torch::Tensor beta,
                      torch::Tensor scale, torch::Tensor out, int64_t groups, int64_t pool,
                      double clamp_min, double clamp_max);

torch::Tensor gn_pool_forward(torch::Tensor x, torch::Tensor gamma, torch::Tensor beta,
                              torch::Tensor scale, int64_t groups, int64_t pool,
                              double clamp_min, double clamp_max) {
    TORCH_CHECK(x.is_cuda(), "x must be CUDA");
    TORCH_CHECK(x.scalar_type() == at::kFloat, "only float32 supported");
    TORCH_CHECK(x.is_contiguous(), "x must be contiguous");
    TORCH_CHECK(gamma.is_cuda() && beta.is_cuda() && scale.is_cuda(), "params must be CUDA");
    const int64_t N = x.size(0);
    const int64_t C = x.size(1);
    const int64_t H = x.size(2);
    const int64_t W = x.size(3);
    const int64_t OH = (H - pool) / pool + 1;
    const int64_t OW = (W - pool) / pool + 1;
    auto stats = torch::empty({N, groups, 2}, x.options());
    auto out = torch::empty({N, C, OH, OW}, x.options());
    gn_stats_cuda(x, stats, groups);
    gn_pool_out_cuda(x, stats, gamma.contiguous(), beta.contiguous(), scale.contiguous(), out,
                     groups, pool, clamp_min, clamp_max);
    return out;
}
'''

_cuda_src = r'''
#include <torch/extension.h>
#include <cuda.h>
#include <cuda_runtime.h>
#include <float.h>

__global__ void gn_stats_kernel(const float* __restrict__ x, float* __restrict__ stats,
                                int N, int C, int H, int W, int G) {
    const int bg = blockIdx.x;
    const int n = bg / G;
    const int g = bg - n * G;
    const int tid = threadIdx.x;
    const int HW = H * W;
    const int CG = C / G;
    const int total = CG * HW;
    float sum = 0.0f;
    float sum2 = 0.0f;
    const int c0 = g * CG;
    const int baseN = n * C * HW;

    for (int i = tid; i < total; i += blockDim.x) {
        int cg = i / HW;
        int hw = i - cg * HW;
        float v = x[baseN + (c0 + cg) * HW + hw];
        sum += v;
        sum2 += v * v;
    }

    __shared__ float ssum[256];
    __shared__ float ssq[256];
    ssum[tid] = sum;
    ssq[tid] = sum2;
    __syncthreads();

    for (int s = blockDim.x >> 1; s > 0; s >>= 1) {
        if (tid < s) {
            ssum[tid] += ssum[tid + s];
            ssq[tid] += ssq[tid + s];
        }
        __syncthreads();
    }
    if (tid == 0) {
        float inv_count = 1.0f / (float)total;
        float mean = ssum[0] * inv_count;
        float var = ssq[0] * inv_count - mean * mean;
        var = var < 0.0f ? 0.0f : var;
        float invstd = rsqrtf(var + 1.0e-5f);
        int off = bg * 2;
        stats[off] = mean;
        stats[off + 1] = invstd;
    }
}

__global__ void gn_pool_out_kernel(const float* __restrict__ x, const float* __restrict__ stats,
                                   const float* __restrict__ gamma, const float* __restrict__ beta,
                                   const float* __restrict__ scale, float* __restrict__ out,
                                   int N, int C, int H, int W, int G, int pool,
                                   int OH, int OW, float clamp_min, float clamp_max) {
    int64_t idx = (int64_t)blockIdx.x * blockDim.x + threadIdx.x;
    const int64_t total = (int64_t)N * C * OH * OW;
    if (idx >= total) return;

    int ow = idx % OW;
    int64_t t = idx / OW;
    int oh = t % OH;
    t /= OH;
    int c = t % C;
    int n = t / C;

    int CG = C / G;
    int g = c / CG;
    int stat_off = (n * G + g) * 2;
    float mean = stats[stat_off];
    float invstd = stats[stat_off + 1];
    float gm = gamma[c];
    float bt = beta[c];
    float sc = scale[c];
    int HW = H * W;
    int base = (n * C + c) * HW + (oh * pool) * W + (ow * pool);

    float m = -FLT_MAX;
    #pragma unroll
    for (int ph = 0; ph < 4; ++ph) {
        #pragma unroll
        for (int pw = 0; pw < 4; ++pw) {
            float v = x[base + ph * W + pw];
            v = ((v - mean) * invstd * gm + bt) * sc;
            m = fmaxf(m, v);
        }
    }
    // Correct for non-4 pool sizes if ever used with same API.
    if (pool != 4) {
        m = -FLT_MAX;
        for (int ph = 0; ph < pool; ++ph) {
            for (int pw = 0; pw < pool; ++pw) {
                float v = x[base + ph * W + pw];
                v = ((v - mean) * invstd * gm + bt) * sc;
                m = fmaxf(m, v);
            }
        }
    }
    m = fminf(fmaxf(m, clamp_min), clamp_max);
    out[idx] = m;
}

void gn_stats_cuda(torch::Tensor x, torch::Tensor stats, int64_t groups) {
    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 dim3 blocks(N * (int)groups);
    const dim3 threads(256);
    gn_stats_kernel<<<blocks, threads>>>(x.data_ptr<float>(), stats.data_ptr<float>(), N, C, H, W, (int)groups);
}

void gn_pool_out_cuda(torch::Tensor x, torch::Tensor stats, torch::Tensor gamma, torch::Tensor beta,
                      torch::Tensor scale, torch::Tensor out, int64_t groups, int64_t pool,
                      double clamp_min, double clamp_max) {
    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 OH = (int)out.size(2);
    const int OW = (int)out.size(3);
    const int64_t total = (int64_t)N * C * OH * OW;
    const int threads = 256;
    const int blocks = (int)((total + threads - 1) / threads);
    gn_pool_out_kernel<<<blocks, threads>>>(x.data_ptr<float>(), stats.data_ptr<float>(),
        gamma.data_ptr<float>(), beta.data_ptr<float>(), scale.data_ptr<float>(), out.data_ptr<float>(),
        N, C, H, W, (int)groups, (int)pool, OH, OW, (float)clamp_min, (float)clamp_max);
}
'''

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

class ModelNew(nn.Module):
    def __init__(self, in_channels, out_channels, kernel_size, num_groups, scale_shape,
                 maxpool_kernel_size, clamp_min, clamp_max):
        super(ModelNew, self).__init__()
        self.conv = nn.Conv2d(in_channels, out_channels, kernel_size)
        self.group_norm = nn.GroupNorm(num_groups, out_channels)
        self.scale = nn.Parameter(torch.ones(scale_shape))
        self.maxpool_kernel_size = maxpool_kernel_size
        self.clamp_min = float(clamp_min)
        self.clamp_max = float(clamp_max)

    def forward(self, x):
        y = F.conv2d(x, self.conv.weight, self.conv.bias, stride=1, padding=0, dilation=1, groups=1)
        y = y.contiguous()
        return _ext.gn_pool_forward(y, self.group_norm.weight, self.group_norm.bias,
                                    self.scale, self.group_norm.num_groups,
                                    int(self.maxpool_kernel_size), self.clamp_min, self.clamp_max)