incorrect
3_ConvTranspose3d_Sum_LayerNorm_AvgPool_GELU.py
started
2026-05-05T02:37:37Z
correctness issue: Output mismatch
timingkernelbench.timing
kernel mean / median / std
—
ref mean / median / std
10.70 / — / 4.88 μs
SOL (speed-of-light)nsight_hardware_counters
roofline / occupancynsight_hardware_counters
dram bandwidth
2980.13 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):
"""
Model that performs a 3D transposed convolution, followed by a sum, layer normalization, average pooling, and GELU activation.
"""
def __init__(self, in_channels, out_channels, kernel_size, stride, padding, output_padding, sum_weight, norm_shape, pool_kernel_size):
super(Model, self).__init__()
self.conv_transpose = nn.ConvTranspose3d(in_channels, out_channels, kernel_size, stride=stride, padding=padding, output_padding=output_padding)
self.sum_weight = nn.Parameter(torch.tensor(sum_weight))
self.norm = nn.LayerNorm(norm_shape)
self.avg_pool = nn.AvgPool3d(kernel_size=pool_kernel_size)
self.gelu = nn.GELU()
def forward(self, x):
x = self.conv_transpose(x)
x = x + self.sum_weight
x = self.norm(x)
x = self.avg_pool(x)
x = self.gelu(x)
return x
batch_size = 32
in_channels = 32
out_channels = 64
depth, height, width = 16, 32, 32
kernel_size = (3, 3, 3)
stride = (2, 2, 2)
padding = (1, 1, 1)
output_padding = (1, 1, 1)
sum_weight = 1.0
norm_shape = (out_channels,)
pool_kernel_size = (2, 2, 2)
def get_inputs():
return [torch.rand(batch_size, in_channels, depth, height, width)]
def get_init_inputs():
return [in_channels, out_channels, kernel_size, stride, padding, output_padding, sum_weight, norm_shape, pool_kernel_size]turn 0 · FINAL · 1 tool call(s) · 84.4s turn · 84.4s LLM · 9,176 chars (click to expand)
turn 084.4s turn · 84.4s LLM · 0.0s tools · FINAL
reasoning
**Evaluating performance options**
I’m considering whether I can use ATen in a C++ host to call at::conv_transpose3d instead of creating a custom solution. It seems that using ATen is the same, but I should definitely think about the performance implications. I want to make sure I’m making a decision that’s efficient and effective, so I’ll need to analyze what might work best in this context before committing to one approach.
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 fused_forward(torch::Tensor x, torch::Tensor weight, torch::Tensor bias,\n torch::Tensor sum_weight, torch::Tensor ln_weight, torch::Tensor ln_bias);\n\"\"\"\n\n_cuda_src = r\"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <math.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_INPUT(x) CHECK_CUDA(x); CHECK_CONTIGUOUS(x)\n\n__global__ __launch_bounds__(64) void conv_ln_pair_kernel(\n const float* __restrict__ x,\n const float* __restrict__ wgt,\n const float* __restrict__ bias,\n const float* __restrict__ sumw,\n const float* __restrict__ gamma,\n const float* __restrict__ beta,\n float* __restrict__ partial,\n int N, int IC, int ID, int IH, int IW, int OC, int OD, int OH, int OW)\n{\n int row = (int)blockIdx.x;\n int tid = (int)threadIdx.x; // 0..63, one complete LayerNorm row over W\n\n int t = row;\n int oh = t % OH; t /= OH;\n int od = t % OD; t /= OD;\n int oc = t % OC; t /= OC;\n int n = t;\n int ow = tid;\n\n float acc = 0.0f;\n\n // ConvTranspose3d with kernel=(3,3,3), stride=(2,2,2), padding=(1,1,1), output_padding=(1,1,1).\n // For a fixed output coordinate, only 1 or 2 input positions per spatial dimension contribute.\n #pragma unroll\n for (int kd = 0; kd < 3; ++kd) {\n int td = od + 1 - kd;\n if ((td & 1) != 0) continue;\n int id = td >> 1;\n if ((unsigned)id >= (unsigned)ID) continue;\n #pragma unroll\n for (int kh = 0; kh < 3; ++kh) {\n int th = oh + 1 - kh;\n if ((th & 1) != 0) continue;\n int ih = th >> 1;\n if ((unsigned)ih >= (unsigned)IH) continue;\n #pragma unroll\n for (int kw = 0; kw < 3; ++kw) {\n int tw = ow + 1 - kw;\n if ((tw & 1) != 0) continue;\n int iw = tw >> 1;\n if ((unsigned)iw >= (unsigned)IW) continue;\n\n for (int ic = 0; ic < IC; ++ic) {\n int xidx = (((n * IC + ic) * ID + id) * IH + ih) * IW + iw;\n int widx = ((((ic * OC + oc) * 3 + kd) * 3 + kh) * 3 + kw);\n acc = fmaf(x[xidx], wgt[widx], acc);\n }\n }\n }\n }\n\n // Bias and scalar add are constant across the normalized W dimension for this row, but include\n // them to mirror PyTorch's finite-precision operation as closely as possible.\n acc += bias[oc] + sumw[0];\n\n __shared__ float vals[64];\n __shared__ float red[64];\n vals[tid] = acc;\n red[tid] = acc;\n __syncthreads();\n\n for (int off = 32; off > 0; off >>= 1) {\n if (tid < off) red[tid] += red[tid + off];\n __syncthreads();\n }\n float mean = red[0] * (1.0f / 64.0f);\n\n float d = acc - mean;\n red[tid] = d * d;\n __syncthreads();\n for (int off = 32; off > 0; off >>= 1) {\n if (tid < off) red[tid] += red[tid + off];\n __syncthreads();\n }\n float inv_std = rsqrtf(red[0] * (1.0f / 64.0f) + 1.0e-5f);\n\n // Store the sum of each adjacent W-pair after LayerNorm. This fuses the W part of AvgPool3d(2).\n if (tid < 32) {\n int w0 = tid << 1;\n float n0 = (vals[w0] - mean) * inv_std * gamma[w0] + beta[w0];\n float n1 = (vals[w0 + 1] - mean) * inv_std * gamma[w0 + 1] + beta[w0 + 1];\n int pidx = ((((n * OC + oc) * OD + od) * OH + oh) * 32 + tid);\n partial[pidx] = n0 + n1;\n }\n}\n\n__global__ void pool_gelu_kernel(\n const float* __restrict__ partial,\n float* __restrict__ out,\n int N, int OC, int OD, int OH)\n{\n int PW = 32;\n int PD = OD >> 1;\n int PH = OH >> 1;\n int total = N * OC * PD * PH * PW;\n int idx = (int)(blockIdx.x * blockDim.x + threadIdx.x);\n if (idx >= total) return;\n\n int t = idx;\n int pw = t % PW; t /= PW;\n int ph = t % PH; t /= PH;\n int pd = t % PD; t /= PD;\n int oc = t % OC; t /= OC;\n int n = t;\n\n int d0 = pd << 1;\n int h0 = ph << 1;\n int base = (((n * OC + oc) * OD + d0) * OH + h0) * PW + pw;\n int stride_h = PW;\n int stride_d = OH * PW;\n float s = partial[base] + partial[base + stride_h] + partial[base + stride_d] + partial[base + stride_d + stride_h];\n float v = s * 0.125f;\n\n // Exact GELU used by nn.GELU(): 0.5*x*(1+erf(x/sqrt(2))).\n float y = 0.5f * v * (1.0f + erff(v * 0.70710678118654752440f));\n out[idx] = y;\n}\n\ntorch::Tensor fused_forward(torch::Tensor x, torch::Tensor weight, torch::Tensor bias,\n torch::Tensor sum_weight, torch::Tensor ln_weight, torch::Tensor ln_bias) {\n CHECK_INPUT(x);\n CHECK_INPUT(weight);\n CHECK_INPUT(bias);\n CHECK_INPUT(sum_weight);\n CHECK_INPUT(ln_weight);\n CHECK_INPUT(ln_bias);\n TORCH_CHECK(x.scalar_type() == torch::kFloat32, \"only float32 is supported\");\n\n const int N = (int)x.size(0);\n const int IC = (int)x.size(1);\n const int ID = (int)x.size(2);\n const int IH = (int)x.size(3);\n const int IW = (int)x.size(4);\n const int OC = (int)weight.size(1);\n const int OD = ID * 2;\n const int OH = IH * 2;\n const int OW = IW * 2;\n TORCH_CHECK(OW == 64, \"specialized LayerNorm width must be 64\");\n\n auto opts = x.options();\n torch::Tensor partial = torch::empty({N, OC, OD, OH, OW / 2}, opts);\n torch::Tensor out = torch::empty({N, OC, OD / 2, OH / 2, OW / 2}, opts);\n\n int rows = N * OC * OD * OH;\n conv_ln_pair_kernel<<<rows, 64>>>(\n x.data_ptr<float>(), weight.data_ptr<float>(), bias.data_ptr<float>(), sum_weight.data_ptr<float>(),\n ln_weight.data_ptr<float>(), ln_bias.data_ptr<float>(), partial.data_ptr<float>(),\n N, IC, ID, IH, IW, OC, OD, OH, OW);\n\n int total = N * OC * (OD / 2) * (OH / 2) * (OW / 2);\n int threads = 256;\n int blocks = (total + threads - 1) / threads;\n pool_gelu_kernel<<<blocks, threads>>>(partial.data_ptr<float>(), out.data_ptr<float>(), N, OC, OD, OH);\n\n return out;\n}\n\"\"\"\n\n_ext = load_inline(\n name=\"fused_deconv_ln_pool_gelu_ext\",\n cpp_sources=_cpp_src,\n cuda_sources=_cuda_src,\n functions=[\"fused_forward\"],\n extra_cflags=[\"-O3\"],\n extra_cuda_cflags=[\"-O3\"],\n verbose=False,\n)\n\nclass ModelNew(nn.Module):\n def __init__(self, in_channels, out_channels, kernel_size, stride, padding, output_padding, sum_weight, norm_shape, pool_kernel_size):\n super(ModelNew, self).__init__()\n self.conv_transpose = nn.ConvTranspose3d(in_channels, out_channels, kernel_size, stride=stride, padding=padding, output_padding=output_padding)\n self.sum_weight = nn.Parameter(torch.tensor(sum_weight))\n self.norm = nn.LayerNorm(norm_shape)\n self.avg_pool = nn.AvgPool3d(kernel_size=pool_kernel_size)\n self.gelu = nn.GELU()\n\n def forward(self, x):\n return _ext.fused_forward(x, self.conv_transpose.weight, self.conv_transpose.bias,\n self.sum_weight, self.norm.weight, self.norm.bias)\n"
}
result ← submit_kernel (fail)
failsubmit_kernel
submit_kernel FAILED: correctness check did not pass ((0 / 5) trials).
correctness_issue: Output mismatch
max_difference: ['0.455415', '0.480748', '0.460920', '0.486956', '0.469968']
avg_difference: ['0.000057', '0.000055', '0.000056', '0.000056', '0.000055']
final submitted kernel (203 lines, 7,185 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 fused_forward(torch::Tensor x, torch::Tensor weight, torch::Tensor bias,
torch::Tensor sum_weight, torch::Tensor ln_weight, torch::Tensor ln_bias);
"""
_cuda_src = r"""
#include <torch/extension.h>
#include <cuda.h>
#include <cuda_runtime.h>
#include <math.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_INPUT(x) CHECK_CUDA(x); CHECK_CONTIGUOUS(x)
__global__ __launch_bounds__(64) void conv_ln_pair_kernel(
const float* __restrict__ x,
const float* __restrict__ wgt,
const float* __restrict__ bias,
const float* __restrict__ sumw,
const float* __restrict__ gamma,
const float* __restrict__ beta,
float* __restrict__ partial,
int N, int IC, int ID, int IH, int IW, int OC, int OD, int OH, int OW)
{
int row = (int)blockIdx.x;
int tid = (int)threadIdx.x; // 0..63, one complete LayerNorm row over W
int t = row;
int oh = t % OH; t /= OH;
int od = t % OD; t /= OD;
int oc = t % OC; t /= OC;
int n = t;
int ow = tid;
float acc = 0.0f;
// ConvTranspose3d with kernel=(3,3,3), stride=(2,2,2), padding=(1,1,1), output_padding=(1,1,1).
// For a fixed output coordinate, only 1 or 2 input positions per spatial dimension contribute.
#pragma unroll
for (int kd = 0; kd < 3; ++kd) {
int td = od + 1 - kd;
if ((td & 1) != 0) continue;
int id = td >> 1;
if ((unsigned)id >= (unsigned)ID) continue;
#pragma unroll
for (int kh = 0; kh < 3; ++kh) {
int th = oh + 1 - kh;
if ((th & 1) != 0) continue;
int ih = th >> 1;
if ((unsigned)ih >= (unsigned)IH) continue;
#pragma unroll
for (int kw = 0; kw < 3; ++kw) {
int tw = ow + 1 - kw;
if ((tw & 1) != 0) continue;
int iw = tw >> 1;
if ((unsigned)iw >= (unsigned)IW) continue;
for (int ic = 0; ic < IC; ++ic) {
int xidx = (((n * IC + ic) * ID + id) * IH + ih) * IW + iw;
int widx = ((((ic * OC + oc) * 3 + kd) * 3 + kh) * 3 + kw);
acc = fmaf(x[xidx], wgt[widx], acc);
}
}
}
}
// Bias and scalar add are constant across the normalized W dimension for this row, but include
// them to mirror PyTorch's finite-precision operation as closely as possible.
acc += bias[oc] + sumw[0];
__shared__ float vals[64];
__shared__ float red[64];
vals[tid] = acc;
red[tid] = acc;
__syncthreads();
for (int off = 32; off > 0; off >>= 1) {
if (tid < off) red[tid] += red[tid + off];
__syncthreads();
}
float mean = red[0] * (1.0f / 64.0f);
float d = acc - mean;
red[tid] = d * d;
__syncthreads();
for (int off = 32; off > 0; off >>= 1) {
if (tid < off) red[tid] += red[tid + off];
__syncthreads();
}
float inv_std = rsqrtf(red[0] * (1.0f / 64.0f) + 1.0e-5f);
// Store the sum of each adjacent W-pair after LayerNorm. This fuses the W part of AvgPool3d(2).
if (tid < 32) {
int w0 = tid << 1;
float n0 = (vals[w0] - mean) * inv_std * gamma[w0] + beta[w0];
float n1 = (vals[w0 + 1] - mean) * inv_std * gamma[w0 + 1] + beta[w0 + 1];
int pidx = ((((n * OC + oc) * OD + od) * OH + oh) * 32 + tid);
partial[pidx] = n0 + n1;
}
}
__global__ void pool_gelu_kernel(
const float* __restrict__ partial,
float* __restrict__ out,
int N, int OC, int OD, int OH)
{
int PW = 32;
int PD = OD >> 1;
int PH = OH >> 1;
int total = N * OC * PD * PH * PW;
int idx = (int)(blockIdx.x * blockDim.x + threadIdx.x);
if (idx >= total) return;
int t = idx;
int pw = t % PW; t /= PW;
int ph = t % PH; t /= PH;
int pd = t % PD; t /= PD;
int oc = t % OC; t /= OC;
int n = t;
int d0 = pd << 1;
int h0 = ph << 1;
int base = (((n * OC + oc) * OD + d0) * OH + h0) * PW + pw;
int stride_h = PW;
int stride_d = OH * PW;
float s = partial[base] + partial[base + stride_h] + partial[base + stride_d] + partial[base + stride_d + stride_h];
float v = s * 0.125f;
// Exact GELU used by nn.GELU(): 0.5*x*(1+erf(x/sqrt(2))).
float y = 0.5f * v * (1.0f + erff(v * 0.70710678118654752440f));
out[idx] = y;
}
torch::Tensor fused_forward(torch::Tensor x, torch::Tensor weight, torch::Tensor bias,
torch::Tensor sum_weight, torch::Tensor ln_weight, torch::Tensor ln_bias) {
CHECK_INPUT(x);
CHECK_INPUT(weight);
CHECK_INPUT(bias);
CHECK_INPUT(sum_weight);
CHECK_INPUT(ln_weight);
CHECK_INPUT(ln_bias);
TORCH_CHECK(x.scalar_type() == torch::kFloat32, "only float32 is supported");
const int N = (int)x.size(0);
const int IC = (int)x.size(1);
const int ID = (int)x.size(2);
const int IH = (int)x.size(3);
const int IW = (int)x.size(4);
const int OC = (int)weight.size(1);
const int OD = ID * 2;
const int OH = IH * 2;
const int OW = IW * 2;
TORCH_CHECK(OW == 64, "specialized LayerNorm width must be 64");
auto opts = x.options();
torch::Tensor partial = torch::empty({N, OC, OD, OH, OW / 2}, opts);
torch::Tensor out = torch::empty({N, OC, OD / 2, OH / 2, OW / 2}, opts);
int rows = N * OC * OD * OH;
conv_ln_pair_kernel<<<rows, 64>>>(
x.data_ptr<float>(), weight.data_ptr<float>(), bias.data_ptr<float>(), sum_weight.data_ptr<float>(),
ln_weight.data_ptr<float>(), ln_bias.data_ptr<float>(), partial.data_ptr<float>(),
N, IC, ID, IH, IW, OC, OD, OH, OW);
int total = N * OC * (OD / 2) * (OH / 2) * (OW / 2);
int threads = 256;
int blocks = (total + threads - 1) / threads;
pool_gelu_kernel<<<blocks, threads>>>(partial.data_ptr<float>(), out.data_ptr<float>(), N, OC, OD, OH);
return out;
}
"""
_ext = load_inline(
name="fused_deconv_ln_pool_gelu_ext",
cpp_sources=_cpp_src,
cuda_sources=_cuda_src,
functions=["fused_forward"],
extra_cflags=["-O3"],
extra_cuda_cflags=["-O3"],
verbose=False,
)
class ModelNew(nn.Module):
def __init__(self, in_channels, out_channels, kernel_size, stride, padding, output_padding, sum_weight, norm_shape, pool_kernel_size):
super(ModelNew, self).__init__()
self.conv_transpose = nn.ConvTranspose3d(in_channels, out_channels, kernel_size, stride=stride, padding=padding, output_padding=output_padding)
self.sum_weight = nn.Parameter(torch.tensor(sum_weight))
self.norm = nn.LayerNorm(norm_shape)
self.avg_pool = nn.AvgPool3d(kernel_size=pool_kernel_size)
self.gelu = nn.GELU()
def forward(self, x):
return _ext.fused_forward(x, self.conv_transpose.weight, self.conv_transpose.bias,
self.sum_weight, self.norm.weight, self.norm.bias)