gpt-5.5-priority · original · l123_single_gpt
incorrect 61_conv_transposed_3D__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
—
ref runtime
9.10 μs
speedup
—
started
2026-05-05T00:19:57Z
correctness issue: Output mismatch
timingkernelbench.timing
kernel runtime
—
ref runtime
9.10 μs
speedup
—
kernel mean / median / std
—
ref mean / median / std
9.10 / — / 0.17 μs
SOL (speed-of-light)nsight_hardware_counters
SOL score (0–1)
0.261
dram util %
5.2%
compute util %
26.1%
bottleneck
compute-bound
dominant pipe
FP32
arithmetic intensity
—
ridge point
20.00
roofline / occupancynsight_hardware_counters
dram bandwidth
173.19 GB/s
dram util %
5.2%
fp32 tflops
—
fp32 util %
—
fp16 tflops
—
occupancy %
40.6%
bottleneck
compute-bound
dominant pipe %
26.1%
arithmetic intensity
—
ridge point
20.00
L1 hit %
—
L2 hit %
85.5%
ld sectors / req
1.41
st sectors / req
—
regs / thread
72
smem / block
1024 B
block size
128
peak bw
3350.00 GB/s
peak fp32 tflops
67.00
peak fp16 tflops
1979.00
warp stalls (top 5)nsight
long_scoreboard
172.4%
short_scoreboard
8.3%
mio_throttle
0.1%
reference PyTorch source (the prompt the agent sees)
import torch
import torch.nn as nn

class Model(nn.Module):
    """
    Performs a transposed 3D convolution with square input and square kernel.

    Args:
        in_channels (int): Number of channels in the input tensor.
        out_channels (int): Number of channels produced by the convolution.
        kernel_size (int): Size of the square convolution kernel.
        stride (int, optional): Stride of the convolution. Defaults to 1.
        padding (int, optional): Padding applied to the input. Defaults to 0.
        output_padding (int, optional): Additional size added to one side of the output shape. Defaults to 0.
        groups (int, optional): Number of blocked connections from input channels to output channels. Defaults to 1.
        bias (bool, optional): If `True`, adds a learnable bias to the output. Defaults to `False`.
    """
    def __init__(self, in_channels: int, out_channels: int, kernel_size: int, stride: int = 1, padding: int = 0, output_padding: int = 0, groups: int = 1, bias: bool = False):
        super(Model, self).__init__()
        self.conv_transpose3d = nn.ConvTranspose3d(in_channels, out_channels, kernel_size=(kernel_size, kernel_size, kernel_size), stride=stride, padding=padding, output_padding=output_padding, groups=groups, bias=bias)
        
    def forward(self, x: torch.Tensor) -> torch.Tensor:
        """
        Performs the transposed 3D convolution.

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

        Returns:
            torch.Tensor: Output tensor of shape (batch_size, out_channels, depth_out, height_out, width_out).
        """
        return self.conv_transpose3d(x)

# Test code
batch_size = 8
in_channels = 48
out_channels = 48
kernel_size = 3
depth = 64
height = 64
width = 64

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

def get_init_inputs():
    return [in_channels, out_channels, kernel_size]  # Provide in_channels, out_channels, kernel_size for initialization
turn 0 · FINAL · 1 tool call(s) · 114.5s turn · 114.5s LLM · 12,972 chars (click to expand)
turn 0114.5s turn · 114.5s LLM · 0.0s tools · FINAL
reasoning
**Exploring tiled matrix multiplication** I'm considering implementing custom matrix multiplication using 16x16 tiles. This could offer some performance improvements, as tiling helps optimize memory usage and cache efficiency! I wonder how well this would work in practice, especially with larger matrices. I should also look into how to handle edge cases, like when the dimensions aren't perfectly divisible by 16. Overall, this approach seems promising, but I'll need to run some tests to see the actual benefits. **Evaluating thread performance** I'm considering how threads perform based on their spatial configurations. If consecutive threads have different operations but the same spatial arrangement, it can lead to poor storage in OutVol. However, if the spatial arrangement is fastest, then consecutive threads can coalesce, allowing each thread to compute and store segments efficiently across the warp. It seems crucial to manage thread spatial organization to enhance overall performance and minimize storage issues.
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 conv_transpose3d_forward(torch::Tensor x, torch::Tensor weight, torch::Tensor bias,\n int stride, int padding, int output_padding, int groups, bool has_bias);\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#define CHECK_FLOAT(x) TORCH_CHECK(x.scalar_type() == at::kFloat, #x \" must be float32\")\n\nconstexpr int VEC = 8;\n\n__global__ __launch_bounds__(128, 2)\nvoid deconv3d_48_k3_s1p0_kernel(const float* __restrict__ x,\n const float* __restrict__ w,\n const float* __restrict__ bias,\n float* __restrict__ y,\n int B, int D, int H, int Wd,\n int Do, int Ho, int Wo,\n int has_bias) {\n const int outVol = Do * Ho * Wo;\n const int inHW = H * Wd;\n const int inVol = D * inHW;\n const int64_t total = (int64_t)B * outVol * 6; // 48 / 8\n\n int64_t job = (int64_t)blockIdx.x * blockDim.x + threadIdx.x;\n if (job >= total) return;\n\n const int spatial = (int)(job % outVol);\n int64_t t = job / outVol;\n const int n = (int)(t % B);\n const int oc_base = (int)(t / B) * VEC;\n\n int rem = spatial;\n const int ow = rem % Wo;\n rem /= Wo;\n const int oh = rem % Ho;\n const int od = rem / Ho;\n\n float acc0 = 0.0f, acc1 = 0.0f, acc2 = 0.0f, acc3 = 0.0f;\n float acc4 = 0.0f, acc5 = 0.0f, acc6 = 0.0f, acc7 = 0.0f;\n\n // Transposed convolution, stride=1, padding=0, kernel=3:\n // y[n,oc,od,oh,ow] = sum_ic,k x[n,ic,od-kd,oh-kh,ow-kw] * W[ic,oc,kd,kh,kw]\n #pragma unroll\n for (int kd = 0; kd < 3; ++kd) {\n const int id = od - kd;\n if ((unsigned)id >= (unsigned)D) continue;\n #pragma unroll\n for (int kh = 0; kh < 3; ++kh) {\n const int ih = oh - kh;\n if ((unsigned)ih >= (unsigned)H) continue;\n #pragma unroll\n for (int kw = 0; kw < 3; ++kw) {\n const int iw = ow - kw;\n if ((unsigned)iw >= (unsigned)Wd) continue;\n const int kidx = (kd * 3 + kh) * 3 + kw;\n const int x_sp = id * inHW + ih * Wd + iw;\n #pragma unroll 4\n for (int ic = 0; ic < 48; ++ic) {\n const float xv = __ldg(x + ((n * 48 + ic) * inVol + x_sp));\n const int wb = ((ic * 48 + oc_base) * 27 + kidx);\n acc0 = fmaf(xv, __ldg(w + wb + 0 * 27), acc0);\n acc1 = fmaf(xv, __ldg(w + wb + 1 * 27), acc1);\n acc2 = fmaf(xv, __ldg(w + wb + 2 * 27), acc2);\n acc3 = fmaf(xv, __ldg(w + wb + 3 * 27), acc3);\n acc4 = fmaf(xv, __ldg(w + wb + 4 * 27), acc4);\n acc5 = fmaf(xv, __ldg(w + wb + 5 * 27), acc5);\n acc6 = fmaf(xv, __ldg(w + wb + 6 * 27), acc6);\n acc7 = fmaf(xv, __ldg(w + wb + 7 * 27), acc7);\n }\n }\n }\n }\n\n if (has_bias) {\n acc0 += __ldg(bias + oc_base + 0);\n acc1 += __ldg(bias + oc_base + 1);\n acc2 += __ldg(bias + oc_base + 2);\n acc3 += __ldg(bias + oc_base + 3);\n acc4 += __ldg(bias + oc_base + 4);\n acc5 += __ldg(bias + oc_base + 5);\n acc6 += __ldg(bias + oc_base + 6);\n acc7 += __ldg(bias + oc_base + 7);\n }\n\n float* yb = y + n * 48 * outVol + spatial;\n yb[(oc_base + 0) * outVol] = acc0;\n yb[(oc_base + 1) * outVol] = acc1;\n yb[(oc_base + 2) * outVol] = acc2;\n yb[(oc_base + 3) * outVol] = acc3;\n yb[(oc_base + 4) * outVol] = acc4;\n yb[(oc_base + 5) * outVol] = acc5;\n yb[(oc_base + 6) * outVol] = acc6;\n yb[(oc_base + 7) * outVol] = acc7;\n}\n\n__global__\nvoid deconv3d_generic_kernel(const float* __restrict__ x,\n const float* __restrict__ w,\n const float* __restrict__ bias,\n float* __restrict__ y,\n int B, int IC, int D, int H, int Wd,\n int OC, int kD, int kH, int kW,\n int Do, int Ho, int Wo,\n int stride, int padding, int groups, int has_bias) {\n const int64_t outVol = (int64_t)Do * Ho * Wo;\n const int64_t total = (int64_t)B * OC * outVol;\n int64_t idx = (int64_t)blockIdx.x * blockDim.x + threadIdx.x;\n if (idx >= total) return;\n\n int spatial = (int)(idx % outVol);\n int64_t tmp = idx / outVol;\n int oc = (int)(tmp % OC);\n int n = (int)(tmp / OC);\n\n int rem = spatial;\n int ow = rem % Wo; rem /= Wo;\n int oh = rem % Ho; int od = rem / Ho;\n\n const int ICpg = IC / groups;\n const int OCpg = OC / groups;\n const int g = oc / OCpg;\n const int woc = oc - g * OCpg;\n const int ic0 = g * ICpg;\n const int ic1 = ic0 + ICpg;\n const int inHW = H * Wd;\n const int inVol = D * inHW;\n float acc = 0.0f;\n\n for (int ic = ic0; ic < ic1; ++ic) {\n for (int kd = 0; kd < kD; ++kd) {\n int td = od + padding - kd;\n if (td < 0 || td % stride) continue;\n int id = td / stride;\n if ((unsigned)id >= (unsigned)D) continue;\n for (int kh = 0; kh < kH; ++kh) {\n int th = oh + padding - kh;\n if (th < 0 || th % stride) continue;\n int ih = th / stride;\n if ((unsigned)ih >= (unsigned)H) continue;\n for (int kw = 0; kw < kW; ++kw) {\n int tw = ow + padding - kw;\n if (tw < 0 || tw % stride) continue;\n int iw = tw / stride;\n if ((unsigned)iw >= (unsigned)Wd) continue;\n float xv = x[((n * IC + ic) * D + id) * inHW + ih * Wd + iw];\n float wv = w[(((ic * OCpg + woc) * kD + kd) * kH + kh) * kW + kw];\n acc = fmaf(xv, wv, acc);\n }\n }\n }\n }\n if (has_bias) acc += bias[oc];\n y[idx] = acc;\n}\n\ntorch::Tensor conv_transpose3d_forward(torch::Tensor x, torch::Tensor weight, torch::Tensor bias,\n int stride, int padding, int output_padding, int groups, bool has_bias) {\n CHECK_CUDA(x);\n CHECK_CUDA(weight);\n CHECK_CONTIGUOUS(x);\n CHECK_CONTIGUOUS(weight);\n CHECK_FLOAT(x);\n CHECK_FLOAT(weight);\n if (has_bias) { CHECK_CUDA(bias); CHECK_CONTIGUOUS(bias); CHECK_FLOAT(bias); }\n\n const int B = (int)x.size(0);\n const int IC = (int)x.size(1);\n const int D = (int)x.size(2);\n const int H = (int)x.size(3);\n const int Wd = (int)x.size(4);\n const int kD = (int)weight.size(2);\n const int kH = (int)weight.size(3);\n const int kW = (int)weight.size(4);\n const int OC = (int)weight.size(1) * groups;\n\n const int Do = (D - 1) * stride - 2 * padding + kD + output_padding;\n const int Ho = (H - 1) * stride - 2 * padding + kH + output_padding;\n const int Wo = (Wd - 1) * stride - 2 * padding + kW + output_padding;\n\n auto y = torch::empty({B, OC, Do, Ho, Wo}, x.options());\n cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n\n if (IC == 48 && OC == 48 && kD == 3 && kH == 3 && kW == 3 && stride == 1 && padding == 0 && output_padding == 0 && groups == 1) {\n const int64_t jobs = (int64_t)B * Do * Ho * Wo * 6;\n const int threads = 128;\n const dim3 blocks((unsigned int)((jobs + threads - 1) / threads));\n deconv3d_48_k3_s1p0_kernel<<<blocks, threads, 0, stream>>>(\n x.data_ptr<float>(), weight.data_ptr<float>(), has_bias ? bias.data_ptr<float>() : nullptr,\n y.data_ptr<float>(), B, D, H, Wd, Do, Ho, Wo, has_bias ? 1 : 0);\n } else {\n const int64_t total = (int64_t)B * OC * Do * Ho * Wo;\n const int threads = 256;\n const dim3 blocks((unsigned int)((total + threads - 1) / threads));\n deconv3d_generic_kernel<<<blocks, threads, 0, stream>>>(\n x.data_ptr<float>(), weight.data_ptr<float>(), has_bias ? bias.data_ptr<float>() : nullptr,\n y.data_ptr<float>(), B, IC, D, H, Wd, OC, kD, kH, kW, Do, Ho, Wo, stride, padding, groups, has_bias ? 1 : 0);\n }\n return y;\n}\n\"\"\"\n\n_ext = load_inline(\n name=\"conv_transpose3d_custom_ext_v1\",\n cpp_sources=_cpp_src,\n cuda_sources=_cuda_src,\n functions=[\"conv_transpose3d_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: int, out_channels: int, kernel_size: int, stride: int = 1, padding: int = 0, output_padding: int = 0, groups: int = 1, bias: bool = False):\n super(ModelNew, self).__init__()\n self.conv_transpose3d = nn.ConvTranspose3d(\n in_channels, out_channels,\n kernel_size=(kernel_size, kernel_size, kernel_size),\n stride=stride, padding=padding, output_padding=output_padding,\n groups=groups, bias=bias\n )\n self.stride = stride if isinstance(stride, int) else stride[0]\n self.padding = padding if isinstance(padding, int) else padding[0]\n self.output_padding = output_padding if isinstance(output_padding, int) else output_padding[0]\n self.groups = groups\n\n def forward(self, x: torch.Tensor) -> torch.Tensor:\n # The benchmark uses contiguous CUDA fp32 tensors. Make parameters contiguous if a caller has\n # altered their layout; this does not change numerical behavior.\n xc = x.contiguous()\n w = self.conv_transpose3d.weight.contiguous()\n b = self.conv_transpose3d.bias\n has_b = b is not None\n if has_b:\n bc = b.contiguous()\n else:\n bc = torch.empty(0, device=xc.device, dtype=xc.dtype)\n return _ext.conv_transpose3d_forward(xc, w, bc, int(self.stride), int(self.padding), int(self.output_padding), int(self.groups), bool(has_b))\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.000469', '0.000475', '0.000487', '0.000452', '0.000465'] avg_difference: ['0.000065', '0.000065', '0.000065', '0.000065', '0.000065']
final submitted kernel (252 lines, 10,320 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 conv_transpose3d_forward(torch::Tensor x, torch::Tensor weight, torch::Tensor bias,
                                       int stride, int padding, int output_padding, int groups, bool has_bias);
"""

_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")
#define CHECK_FLOAT(x) TORCH_CHECK(x.scalar_type() == at::kFloat, #x " must be float32")

constexpr int VEC = 8;

__global__ __launch_bounds__(128, 2)
void deconv3d_48_k3_s1p0_kernel(const float* __restrict__ x,
                                const float* __restrict__ w,
                                const float* __restrict__ bias,
                                float* __restrict__ y,
                                int B, int D, int H, int Wd,
                                int Do, int Ho, int Wo,
                                int has_bias) {
    const int outVol = Do * Ho * Wo;
    const int inHW = H * Wd;
    const int inVol = D * inHW;
    const int64_t total = (int64_t)B * outVol * 6; // 48 / 8

    int64_t job = (int64_t)blockIdx.x * blockDim.x + threadIdx.x;
    if (job >= total) return;

    const int spatial = (int)(job % outVol);
    int64_t t = job / outVol;
    const int n = (int)(t % B);
    const int oc_base = (int)(t / B) * VEC;

    int rem = spatial;
    const int ow = rem % Wo;
    rem /= Wo;
    const int oh = rem % Ho;
    const int od = rem / Ho;

    float acc0 = 0.0f, acc1 = 0.0f, acc2 = 0.0f, acc3 = 0.0f;
    float acc4 = 0.0f, acc5 = 0.0f, acc6 = 0.0f, acc7 = 0.0f;

    // Transposed convolution, stride=1, padding=0, kernel=3:
    // y[n,oc,od,oh,ow] = sum_ic,k x[n,ic,od-kd,oh-kh,ow-kw] * W[ic,oc,kd,kh,kw]
    #pragma unroll
    for (int kd = 0; kd < 3; ++kd) {
        const int id = od - kd;
        if ((unsigned)id >= (unsigned)D) continue;
        #pragma unroll
        for (int kh = 0; kh < 3; ++kh) {
            const int ih = oh - kh;
            if ((unsigned)ih >= (unsigned)H) continue;
            #pragma unroll
            for (int kw = 0; kw < 3; ++kw) {
                const int iw = ow - kw;
                if ((unsigned)iw >= (unsigned)Wd) continue;
                const int kidx = (kd * 3 + kh) * 3 + kw;
                const int x_sp = id * inHW + ih * Wd + iw;
                #pragma unroll 4
                for (int ic = 0; ic < 48; ++ic) {
                    const float xv = __ldg(x + ((n * 48 + ic) * inVol + x_sp));
                    const int wb = ((ic * 48 + oc_base) * 27 + kidx);
                    acc0 = fmaf(xv, __ldg(w + wb + 0 * 27), acc0);
                    acc1 = fmaf(xv, __ldg(w + wb + 1 * 27), acc1);
                    acc2 = fmaf(xv, __ldg(w + wb + 2 * 27), acc2);
                    acc3 = fmaf(xv, __ldg(w + wb + 3 * 27), acc3);
                    acc4 = fmaf(xv, __ldg(w + wb + 4 * 27), acc4);
                    acc5 = fmaf(xv, __ldg(w + wb + 5 * 27), acc5);
                    acc6 = fmaf(xv, __ldg(w + wb + 6 * 27), acc6);
                    acc7 = fmaf(xv, __ldg(w + wb + 7 * 27), acc7);
                }
            }
        }
    }

    if (has_bias) {
        acc0 += __ldg(bias + oc_base + 0);
        acc1 += __ldg(bias + oc_base + 1);
        acc2 += __ldg(bias + oc_base + 2);
        acc3 += __ldg(bias + oc_base + 3);
        acc4 += __ldg(bias + oc_base + 4);
        acc5 += __ldg(bias + oc_base + 5);
        acc6 += __ldg(bias + oc_base + 6);
        acc7 += __ldg(bias + oc_base + 7);
    }

    float* yb = y + n * 48 * outVol + spatial;
    yb[(oc_base + 0) * outVol] = acc0;
    yb[(oc_base + 1) * outVol] = acc1;
    yb[(oc_base + 2) * outVol] = acc2;
    yb[(oc_base + 3) * outVol] = acc3;
    yb[(oc_base + 4) * outVol] = acc4;
    yb[(oc_base + 5) * outVol] = acc5;
    yb[(oc_base + 6) * outVol] = acc6;
    yb[(oc_base + 7) * outVol] = acc7;
}

__global__
void deconv3d_generic_kernel(const float* __restrict__ x,
                             const float* __restrict__ w,
                             const float* __restrict__ bias,
                             float* __restrict__ y,
                             int B, int IC, int D, int H, int Wd,
                             int OC, int kD, int kH, int kW,
                             int Do, int Ho, int Wo,
                             int stride, int padding, int groups, int has_bias) {
    const int64_t outVol = (int64_t)Do * Ho * Wo;
    const int64_t total = (int64_t)B * OC * outVol;
    int64_t idx = (int64_t)blockIdx.x * blockDim.x + threadIdx.x;
    if (idx >= total) return;

    int spatial = (int)(idx % outVol);
    int64_t tmp = idx / outVol;
    int oc = (int)(tmp % OC);
    int n = (int)(tmp / OC);

    int rem = spatial;
    int ow = rem % Wo; rem /= Wo;
    int oh = rem % Ho; int od = rem / Ho;

    const int ICpg = IC / groups;
    const int OCpg = OC / groups;
    const int g = oc / OCpg;
    const int woc = oc - g * OCpg;
    const int ic0 = g * ICpg;
    const int ic1 = ic0 + ICpg;
    const int inHW = H * Wd;
    const int inVol = D * inHW;
    float acc = 0.0f;

    for (int ic = ic0; ic < ic1; ++ic) {
        for (int kd = 0; kd < kD; ++kd) {
            int td = od + padding - kd;
            if (td < 0 || td % stride) continue;
            int id = td / stride;
            if ((unsigned)id >= (unsigned)D) continue;
            for (int kh = 0; kh < kH; ++kh) {
                int th = oh + padding - kh;
                if (th < 0 || th % stride) continue;
                int ih = th / stride;
                if ((unsigned)ih >= (unsigned)H) continue;
                for (int kw = 0; kw < kW; ++kw) {
                    int tw = ow + padding - kw;
                    if (tw < 0 || tw % stride) continue;
                    int iw = tw / stride;
                    if ((unsigned)iw >= (unsigned)Wd) continue;
                    float xv = x[((n * IC + ic) * D + id) * inHW + ih * Wd + iw];
                    float wv = w[(((ic * OCpg + woc) * kD + kd) * kH + kh) * kW + kw];
                    acc = fmaf(xv, wv, acc);
                }
            }
        }
    }
    if (has_bias) acc += bias[oc];
    y[idx] = acc;
}

torch::Tensor conv_transpose3d_forward(torch::Tensor x, torch::Tensor weight, torch::Tensor bias,
                                       int stride, int padding, int output_padding, int groups, bool has_bias) {
    CHECK_CUDA(x);
    CHECK_CUDA(weight);
    CHECK_CONTIGUOUS(x);
    CHECK_CONTIGUOUS(weight);
    CHECK_FLOAT(x);
    CHECK_FLOAT(weight);
    if (has_bias) { CHECK_CUDA(bias); CHECK_CONTIGUOUS(bias); CHECK_FLOAT(bias); }

    const int B  = (int)x.size(0);
    const int IC = (int)x.size(1);
    const int D  = (int)x.size(2);
    const int H  = (int)x.size(3);
    const int Wd = (int)x.size(4);
    const int kD = (int)weight.size(2);
    const int kH = (int)weight.size(3);
    const int kW = (int)weight.size(4);
    const int OC = (int)weight.size(1) * groups;

    const int Do = (D - 1) * stride - 2 * padding + kD + output_padding;
    const int Ho = (H - 1) * stride - 2 * padding + kH + output_padding;
    const int Wo = (Wd - 1) * stride - 2 * padding + kW + output_padding;

    auto y = torch::empty({B, OC, Do, Ho, Wo}, x.options());
    cudaStream_t stream = at::cuda::getCurrentCUDAStream();

    if (IC == 48 && OC == 48 && kD == 3 && kH == 3 && kW == 3 && stride == 1 && padding == 0 && output_padding == 0 && groups == 1) {
        const int64_t jobs = (int64_t)B * Do * Ho * Wo * 6;
        const int threads = 128;
        const dim3 blocks((unsigned int)((jobs + threads - 1) / threads));
        deconv3d_48_k3_s1p0_kernel<<<blocks, threads, 0, stream>>>(
            x.data_ptr<float>(), weight.data_ptr<float>(), has_bias ? bias.data_ptr<float>() : nullptr,
            y.data_ptr<float>(), B, D, H, Wd, Do, Ho, Wo, has_bias ? 1 : 0);
    } else {
        const int64_t total = (int64_t)B * OC * Do * Ho * Wo;
        const int threads = 256;
        const dim3 blocks((unsigned int)((total + threads - 1) / threads));
        deconv3d_generic_kernel<<<blocks, threads, 0, stream>>>(
            x.data_ptr<float>(), weight.data_ptr<float>(), has_bias ? bias.data_ptr<float>() : nullptr,
            y.data_ptr<float>(), B, IC, D, H, Wd, OC, kD, kH, kW, Do, Ho, Wo, stride, padding, groups, has_bias ? 1 : 0);
    }
    return y;
}
"""

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

class ModelNew(nn.Module):
    def __init__(self, in_channels: int, out_channels: int, kernel_size: int, stride: int = 1, padding: int = 0, output_padding: int = 0, groups: int = 1, bias: bool = False):
        super(ModelNew, self).__init__()
        self.conv_transpose3d = nn.ConvTranspose3d(
            in_channels, out_channels,
            kernel_size=(kernel_size, kernel_size, kernel_size),
            stride=stride, padding=padding, output_padding=output_padding,
            groups=groups, bias=bias
        )
        self.stride = stride if isinstance(stride, int) else stride[0]
        self.padding = padding if isinstance(padding, int) else padding[0]
        self.output_padding = output_padding if isinstance(output_padding, int) else output_padding[0]
        self.groups = groups

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        # The benchmark uses contiguous CUDA fp32 tensors.  Make parameters contiguous if a caller has
        # altered their layout; this does not change numerical behavior.
        xc = x.contiguous()
        w = self.conv_transpose3d.weight.contiguous()
        b = self.conv_transpose3d.bias
        has_b = b is not None
        if has_b:
            bc = b.contiguous()
        else:
            bc = torch.empty(0, device=xc.device, dtype=xc.dtype)
        return _ext.conv_transpose3d_forward(xc, w, bc, int(self.stride), int(self.padding), int(self.output_padding), int(self.groups), bool(has_b))