gpt-5.5-priority · original · l123_single_gpt
correct 72_conv_transposed_3D_asymmetric_input_asymmetric_kernel___strided_padded_grouped_.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
4.21 μs
ref runtime
2.82 μs
speedup
0.67x
started
2026-05-05T00:14:24Z
timingkernelbench.timing
kernel runtime
4.21 μs
ref runtime
2.82 μs
speedup
0.67x
kernel mean / median / std
4.21 / — / 0.00 μs
ref mean / median / std
2.82 / — / 0.02 μs
numerical precisioneval_kernel_against_ref
max abs error
5.36e-07
mean abs error
1.55e-08
max rel error
10939.62
mean rel error
1.29e-04
gpu memoryextended_metrics.measure_memory
kernel peak
154.71 MB
ref peak
154.71 MB
kernel / ref
1.00x
kernel launches / fusiontorch.profiler
kernel launches
2
ref launches
4
fusion ratio (ref / kernel)
2.00x
total cuda time
45 μs
energyNVML (pynvml)
kernel mJ / run
488.14
ref mJ / run
243.74
ratio (ref / kernel)
0.50x
avg power
115.85 W
ref avg power
86.47 W
SOL (speed-of-light)nsight_hardware_counters
SOL score (0–1)
0.255
dram util %
0.8%
compute util %
25.5%
bottleneck
compute-bound
dominant pipe
FP32
arithmetic intensity
—
ridge point
20.00
roofline / occupancynsight_hardware_counters
dram bandwidth
24.99 GB/s
dram util %
0.7%
fp32 tflops
—
fp32 util %
—
fp16 tflops
—
occupancy %
45.6%
bottleneck
compute-bound
dominant pipe %
25.5%
arithmetic intensity
—
ridge point
20.00
L1 hit %
—
L2 hit %
97.2%
ld sectors / req
1.76
st sectors / req
—
regs / thread
56
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
143.0%
short_scoreboard
5.8%
mio_throttle
0.0%
reference PyTorch source (the prompt the agent sees)
import torch
import torch.nn as nn

class Model(nn.Module):
    """
    Performs a 3D transposed convolution operation with asymmetric input and kernel, and optional stride.

    Args:
        in_channels (int): Number of channels in the input tensor.
        out_channels (int): Number of channels produced by the convolution.
        kernel_size (tuple of ints): Size of the convolution kernel in the form (kernel_size_depth, kernel_size_height, kernel_size_width).
        stride (tuple of ints, optional): Stride of the convolution in the form (stride_depth, stride_height, stride_width). Defaults to (1, 1, 1).
        padding (tuple of ints, optional): Padding applied to the input in the form (padding_depth, padding_height, padding_width). Defaults to (0, 0, 0).
        output_padding (tuple of ints, optional): Additional size added to one side of the output shape. Defaults to (0, 0, 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: tuple, stride: tuple = (1, 1, 1), padding: tuple = (0, 0, 0), output_padding: tuple = (0, 0, 0), groups: int = 1, bias: bool = False):
        super(Model, self).__init__()
        self.conv_transpose3d = nn.ConvTranspose3d(in_channels, out_channels, kernel_size, stride=stride, padding=padding, output_padding=output_padding, groups=groups, bias=bias)

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        """
        Performs the 3D transposed 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 = 32
out_channels = 32
kernel_size = (3, 5, 7)
depth = 12
height = 24
width = 48
stride = (2, 2, 2)
padding = (1, 2, 3)
output_padding = (1, 1, 1)
groups = 4

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, stride, padding, output_padding, groups]
turn 0 · FINAL · 1 tool call(s) · 74.0s turn · 74.0s LLM · 12,741 chars (click to expand)
turn 074.0s turn · 74.0s LLM · 0.0s tools · FINAL
reasoning
**Exploring vectorization strategy** I’m thinking about using vectorization over oc, possibly with one thread per output. For each oc, I’d group them, and the weight indexing would depend on ic and oc_g. I wonder if the neighboring oc threads could read the same x but have different weights. Maybe I should consider blocking oc too? It feels like there are multiple angles to explore, and I want to make sure I'm covering all the bases to optimize performance! **Checking code structure** I’m considering that the constructor can typically accept the same parameters. However, I should store those values, and if there’s a mismatch, perhaps I can handle it generically. I think the code complexity is manageable for this situation, so it should be fine as long as I keep things clear and straightforward. I want to ensure that the implementation stays tidy and doesn’t become overly complicated while still functioning effectively.
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_cuda(torch::Tensor x, torch::Tensor weight, torch::Tensor bias,\n int sd, int sh, int sw,\n int pd, int ph, int pw,\n int opd, int oph, int opw,\n int groups);\n\ntorch::Tensor conv_transpose3d_forward(torch::Tensor x, torch::Tensor weight, torch::Tensor bias,\n int sd, int sh, int sw,\n int pd, int ph, int pw,\n int opd, int oph, int opw,\n int groups) {\n return conv_transpose3d_cuda(x, weight, bias, sd, sh, sw, pd, ph, pw, opd, oph, opw, groups);\n}\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\nstatic __forceinline__ __device__ int ceil_div_pos_maybe(int a, int b) {\n // only used with b > 0; if a <= 0 the clamped lower bound is 0\n return (a <= 0) ? 0 : ((a + b - 1) / b);\n}\n\ntemplate <bool HAS_BIAS>\n__global__ void convt3d_s2_g4_k357_kernel(const float* __restrict__ x,\n const float* __restrict__ w,\n const float* __restrict__ bias,\n float* __restrict__ y,\n int total) {\n // Specialized for the benchmark case:\n // N x 32 x 12 x 24 x 48 -> N x 32 x 24 x 48 x 96\n const int W_in = 48, H_in = 24, D_in = 12;\n const int C_in = 32, C_out = 32;\n const int OW = 96, OH = 48, OD = 24;\n const int Cout_per_g = 8, Cin_per_g = 8;\n const int KD = 3, KH = 5, KW = 7;\n const int padD = 1, padH = 2, padW = 3;\n\n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n if (idx >= total) return;\n\n int t = idx;\n int ow = t % OW; t /= OW;\n int oh = t % OH; t /= OH;\n int od = t % OD; t /= OD;\n int oc = t % C_out;\n int n = t / C_out;\n\n int g = oc >> 3;\n int ocg = oc & 7;\n int ic_base = g << 3;\n\n int td = od + padD;\n int th = oh + padH;\n int tw = ow + padW;\n\n int id0 = ceil_div_pos_maybe(td - (KD - 1), 2);\n int ih0 = ceil_div_pos_maybe(th - (KH - 1), 2);\n int iw0 = ceil_div_pos_maybe(tw - (KW - 1), 2);\n int id1 = td >> 1; if (id1 > D_in - 1) id1 = D_in - 1;\n int ih1 = th >> 1; if (ih1 > H_in - 1) ih1 = H_in - 1;\n int iw1 = tw >> 1; if (iw1 > W_in - 1) iw1 = W_in - 1;\n\n float acc = HAS_BIAS ? __ldg(bias + oc) : 0.0f;\n\n #pragma unroll\n for (int icg = 0; icg < Cin_per_g; ++icg) {\n int ic = ic_base + icg;\n const float* x_ic = x + (((n * C_in + ic) * D_in) * H_in) * W_in;\n const float* w_ic = w + (((ic * Cout_per_g + ocg) * KD) * KH) * KW;\n\n for (int id = id0; id <= id1; ++id) {\n int kd = td - (id << 1);\n const float* x_d = x_ic + (id * H_in) * W_in;\n const float* w_d = w_ic + (kd * KH) * KW;\n for (int ih = ih0; ih <= ih1; ++ih) {\n int kh = th - (ih << 1);\n const float* x_h = x_d + ih * W_in;\n const float* w_h = w_d + kh * KW;\n #pragma unroll 4\n for (int iw = iw0; iw <= iw1; ++iw) {\n int kw = tw - (iw << 1);\n acc = fmaf(__ldg(x_h + iw), __ldg(w_h + kw), acc);\n }\n }\n }\n }\n y[idx] = acc;\n}\n\ntemplate <bool HAS_BIAS>\n__global__ void convt3d_generic_kernel(const float* __restrict__ x,\n const float* __restrict__ w,\n const float* __restrict__ bias,\n float* __restrict__ y,\n int total,\n int N, int C_in, int D_in, int H_in, int W_in,\n int C_out, int OD, int OH, int OW,\n int KD, int KH, int KW,\n int sd, int sh, int sw,\n int pd, int ph, int pw,\n int groups) {\n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n if (idx >= total) return;\n int tmp = idx;\n int ow = tmp % OW; tmp /= OW;\n int oh = tmp % OH; tmp /= OH;\n int od = tmp % OD; tmp /= OD;\n int oc = tmp % C_out;\n int n = tmp / C_out;\n\n int cout_pg = C_out / groups;\n int cin_pg = C_in / groups;\n int g = oc / cout_pg;\n int ocg = oc - g * cout_pg;\n int ic_base = g * cin_pg;\n\n int td = od + pd, th = oh + ph, tw = ow + pw;\n int id0 = ceil_div_pos_maybe(td - (KD - 1), sd);\n int ih0 = ceil_div_pos_maybe(th - (KH - 1), sh);\n int iw0 = ceil_div_pos_maybe(tw - (KW - 1), sw);\n int id1 = td / sd; if (id1 > D_in - 1) id1 = D_in - 1;\n int ih1 = th / sh; if (ih1 > H_in - 1) ih1 = H_in - 1;\n int iw1 = tw / sw; if (iw1 > W_in - 1) iw1 = W_in - 1;\n\n float acc = HAS_BIAS ? __ldg(bias + oc) : 0.0f;\n for (int icg = 0; icg < cin_pg; ++icg) {\n int ic = ic_base + icg;\n const float* x_ic = x + (((n * C_in + ic) * D_in) * H_in) * W_in;\n const float* w_ic = w + (((ic * cout_pg + ocg) * KD) * KH) * KW;\n for (int id = id0; id <= id1; ++id) {\n int kd = td - id * sd;\n const float* x_d = x_ic + (id * H_in) * W_in;\n const float* w_d = w_ic + (kd * KH) * KW;\n for (int ih = ih0; ih <= ih1; ++ih) {\n int kh = th - ih * sh;\n const float* x_h = x_d + ih * W_in;\n const float* w_h = w_d + kh * KW;\n for (int iw = iw0; iw <= iw1; ++iw) {\n int kw = tw - iw * sw;\n acc = fmaf(__ldg(x_h + iw), __ldg(w_h + kw), acc);\n }\n }\n }\n }\n y[idx] = acc;\n}\n\ntorch::Tensor conv_transpose3d_cuda(torch::Tensor x, torch::Tensor weight, torch::Tensor bias,\n int sd, int sh, int sw,\n int pd, int ph, int pw,\n int opd, int oph, int opw,\n int groups) {\n x = x.contiguous();\n weight = weight.contiguous();\n if (bias.numel() != 0) bias = bias.contiguous();\n\n const int N = (int)x.size(0);\n const int C_in = (int)x.size(1);\n const int D_in = (int)x.size(2);\n const int H_in = (int)x.size(3);\n const int W_in = (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 C_out = (int)weight.size(1) * groups;\n const int OD = (D_in - 1) * sd - 2 * pd + KD + opd;\n const int OH = (H_in - 1) * sh - 2 * ph + KH + oph;\n const int OW = (W_in - 1) * sw - 2 * pw + KW + opw;\n\n auto y = torch::empty({N, C_out, OD, OH, OW}, x.options());\n int total = N * C_out * OD * OH * OW;\n const int threads = 256;\n const dim3 blocks((total + threads - 1) / threads);\n cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n bool has_bias = (bias.numel() != 0);\n\n if (C_in == 32 && C_out == 32 && D_in == 12 && H_in == 24 && W_in == 48 &&\n KD == 3 && KH == 5 && KW == 7 && sd == 2 && sh == 2 && sw == 2 &&\n pd == 1 && ph == 2 && pw == 3 && opd == 1 && oph == 1 && opw == 1 &&\n groups == 4 && OD == 24 && OH == 48 && OW == 96) {\n if (has_bias) convt3d_s2_g4_k357_kernel<true><<<blocks, threads, 0, stream>>>(x.data_ptr<float>(), weight.data_ptr<float>(), bias.data_ptr<float>(), y.data_ptr<float>(), total);\n else convt3d_s2_g4_k357_kernel<false><<<blocks, threads, 0, stream>>>(x.data_ptr<float>(), weight.data_ptr<float>(), nullptr, y.data_ptr<float>(), total);\n } else {\n if (has_bias) convt3d_generic_kernel<true><<<blocks, threads, 0, stream>>>(x.data_ptr<float>(), weight.data_ptr<float>(), bias.data_ptr<float>(), y.data_ptr<float>(), total, N, C_in, D_in, H_in, W_in, C_out, OD, OH, OW, KD, KH, KW, sd, sh, sw, pd, ph, pw, groups);\n else convt3d_generic_kernel<false><<<blocks, threads, 0, stream>>>(x.data_ptr<float>(), weight.data_ptr<float>(), nullptr, y.data_ptr<float>(), total, N, C_in, D_in, H_in, W_in, C_out, OD, OH, OW, KD, KH, KW, sd, sh, sw, pd, ph, pw, groups);\n }\n return y;\n}\n'''\n\n_ext = load_inline(\n name='convt3d_custom_ext_v1',\n cpp_sources=_cpp_src,\n cuda_sources=_cuda_src,\n functions=['conv_transpose3d_forward'],\n extra_cuda_cflags=['-O3'],\n extra_cflags=['-O3'],\n verbose=False,\n)\n\nclass ModelNew(nn.Module):\n def __init__(self, in_channels: int, out_channels: int, kernel_size: tuple, stride: tuple = (1, 1, 1), padding: tuple = (0, 0, 0), output_padding: tuple = (0, 0, 0), groups: int = 1, bias: bool = False):\n super(ModelNew, self).__init__()\n self.conv_transpose3d = nn.ConvTranspose3d(in_channels, out_channels, kernel_size, stride=stride, padding=padding, output_padding=output_padding, groups=groups, bias=bias)\n self.stride = tuple(stride)\n self.padding = tuple(padding)\n self.output_padding = tuple(output_padding)\n self.groups = groups\n\n def forward(self, x: torch.Tensor) -> torch.Tensor:\n b = self.conv_transpose3d.bias\n if b is None:\n b = torch.empty(0, device=x.device, dtype=x.dtype)\n s = self.stride; p = self.padding; op = self.output_padding\n return _ext.conv_transpose3d_forward(x, self.conv_transpose3d.weight, b,\n int(s[0]), int(s[1]), int(s[2]),\n int(p[0]), int(p[1]), int(p[2]),\n int(op[0]), int(op[1]), int(op[2]),\n int(self.groups))\n" }
result ← submit_kernel (ok)
oksubmit_kernel submit_kernel PASSED: (5 / 5) correctness trials all passed. Kernel runtime: 4.21 μs Runtime stats: mean=4.21μs median=?μs std=0.00μs Numerical precision: max_abs_err=5.36e-07 mean_abs_err=1.55e-08 max_rel_err=1.09e+04 Memory: 154.71 MB peak (ref: 154.71 MB, ratio: 1.0x) Kernel launches: 2 (ref: 4, fusion ratio: 2.0) Energy: 488.14 mJ/run (ref: 243.74 mJ, ratio: 0.4993x) SOL score: 0.2546
final submitted kernel (231 lines, 9,998 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_cuda(torch::Tensor x, torch::Tensor weight, torch::Tensor bias,
                                    int sd, int sh, int sw,
                                    int pd, int ph, int pw,
                                    int opd, int oph, int opw,
                                    int groups);

torch::Tensor conv_transpose3d_forward(torch::Tensor x, torch::Tensor weight, torch::Tensor bias,
                                       int sd, int sh, int sw,
                                       int pd, int ph, int pw,
                                       int opd, int oph, int opw,
                                       int groups) {
    return conv_transpose3d_cuda(x, weight, bias, sd, sh, sw, pd, ph, pw, opd, oph, opw, groups);
}
'''

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

static __forceinline__ __device__ int ceil_div_pos_maybe(int a, int b) {
    // only used with b > 0; if a <= 0 the clamped lower bound is 0
    return (a <= 0) ? 0 : ((a + b - 1) / b);
}

template <bool HAS_BIAS>
__global__ void convt3d_s2_g4_k357_kernel(const float* __restrict__ x,
                                           const float* __restrict__ w,
                                           const float* __restrict__ bias,
                                           float* __restrict__ y,
                                           int total) {
    // Specialized for the benchmark case:
    // N x 32 x 12 x 24 x 48 -> N x 32 x 24 x 48 x 96
    const int W_in = 48, H_in = 24, D_in = 12;
    const int C_in = 32, C_out = 32;
    const int OW = 96, OH = 48, OD = 24;
    const int Cout_per_g = 8, Cin_per_g = 8;
    const int KD = 3, KH = 5, KW = 7;
    const int padD = 1, padH = 2, padW = 3;

    int idx = blockIdx.x * blockDim.x + threadIdx.x;
    if (idx >= total) return;

    int t = idx;
    int ow = t % OW; t /= OW;
    int oh = t % OH; t /= OH;
    int od = t % OD; t /= OD;
    int oc = t % C_out;
    int n  = t / C_out;

    int g = oc >> 3;
    int ocg = oc & 7;
    int ic_base = g << 3;

    int td = od + padD;
    int th = oh + padH;
    int tw = ow + padW;

    int id0 = ceil_div_pos_maybe(td - (KD - 1), 2);
    int ih0 = ceil_div_pos_maybe(th - (KH - 1), 2);
    int iw0 = ceil_div_pos_maybe(tw - (KW - 1), 2);
    int id1 = td >> 1; if (id1 > D_in - 1) id1 = D_in - 1;
    int ih1 = th >> 1; if (ih1 > H_in - 1) ih1 = H_in - 1;
    int iw1 = tw >> 1; if (iw1 > W_in - 1) iw1 = W_in - 1;

    float acc = HAS_BIAS ? __ldg(bias + oc) : 0.0f;

    #pragma unroll
    for (int icg = 0; icg < Cin_per_g; ++icg) {
        int ic = ic_base + icg;
        const float* x_ic = x + (((n * C_in + ic) * D_in) * H_in) * W_in;
        const float* w_ic = w + (((ic * Cout_per_g + ocg) * KD) * KH) * KW;

        for (int id = id0; id <= id1; ++id) {
            int kd = td - (id << 1);
            const float* x_d = x_ic + (id * H_in) * W_in;
            const float* w_d = w_ic + (kd * KH) * KW;
            for (int ih = ih0; ih <= ih1; ++ih) {
                int kh = th - (ih << 1);
                const float* x_h = x_d + ih * W_in;
                const float* w_h = w_d + kh * KW;
                #pragma unroll 4
                for (int iw = iw0; iw <= iw1; ++iw) {
                    int kw = tw - (iw << 1);
                    acc = fmaf(__ldg(x_h + iw), __ldg(w_h + kw), acc);
                }
            }
        }
    }
    y[idx] = acc;
}

template <bool HAS_BIAS>
__global__ void convt3d_generic_kernel(const float* __restrict__ x,
                                        const float* __restrict__ w,
                                        const float* __restrict__ bias,
                                        float* __restrict__ y,
                                        int total,
                                        int N, int C_in, int D_in, int H_in, int W_in,
                                        int C_out, int OD, int OH, int OW,
                                        int KD, int KH, int KW,
                                        int sd, int sh, int sw,
                                        int pd, int ph, int pw,
                                        int groups) {
    int idx = blockIdx.x * blockDim.x + threadIdx.x;
    if (idx >= total) return;
    int tmp = idx;
    int ow = tmp % OW; tmp /= OW;
    int oh = tmp % OH; tmp /= OH;
    int od = tmp % OD; tmp /= OD;
    int oc = tmp % C_out;
    int n = tmp / C_out;

    int cout_pg = C_out / groups;
    int cin_pg = C_in / groups;
    int g = oc / cout_pg;
    int ocg = oc - g * cout_pg;
    int ic_base = g * cin_pg;

    int td = od + pd, th = oh + ph, tw = ow + pw;
    int id0 = ceil_div_pos_maybe(td - (KD - 1), sd);
    int ih0 = ceil_div_pos_maybe(th - (KH - 1), sh);
    int iw0 = ceil_div_pos_maybe(tw - (KW - 1), sw);
    int id1 = td / sd; if (id1 > D_in - 1) id1 = D_in - 1;
    int ih1 = th / sh; if (ih1 > H_in - 1) ih1 = H_in - 1;
    int iw1 = tw / sw; if (iw1 > W_in - 1) iw1 = W_in - 1;

    float acc = HAS_BIAS ? __ldg(bias + oc) : 0.0f;
    for (int icg = 0; icg < cin_pg; ++icg) {
        int ic = ic_base + icg;
        const float* x_ic = x + (((n * C_in + ic) * D_in) * H_in) * W_in;
        const float* w_ic = w + (((ic * cout_pg + ocg) * KD) * KH) * KW;
        for (int id = id0; id <= id1; ++id) {
            int kd = td - id * sd;
            const float* x_d = x_ic + (id * H_in) * W_in;
            const float* w_d = w_ic + (kd * KH) * KW;
            for (int ih = ih0; ih <= ih1; ++ih) {
                int kh = th - ih * sh;
                const float* x_h = x_d + ih * W_in;
                const float* w_h = w_d + kh * KW;
                for (int iw = iw0; iw <= iw1; ++iw) {
                    int kw = tw - iw * sw;
                    acc = fmaf(__ldg(x_h + iw), __ldg(w_h + kw), acc);
                }
            }
        }
    }
    y[idx] = acc;
}

torch::Tensor conv_transpose3d_cuda(torch::Tensor x, torch::Tensor weight, torch::Tensor bias,
                                    int sd, int sh, int sw,
                                    int pd, int ph, int pw,
                                    int opd, int oph, int opw,
                                    int groups) {
    x = x.contiguous();
    weight = weight.contiguous();
    if (bias.numel() != 0) bias = bias.contiguous();

    const int N = (int)x.size(0);
    const int C_in = (int)x.size(1);
    const int D_in = (int)x.size(2);
    const int H_in = (int)x.size(3);
    const int W_in = (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 C_out = (int)weight.size(1) * groups;
    const int OD = (D_in - 1) * sd - 2 * pd + KD + opd;
    const int OH = (H_in - 1) * sh - 2 * ph + KH + oph;
    const int OW = (W_in - 1) * sw - 2 * pw + KW + opw;

    auto y = torch::empty({N, C_out, OD, OH, OW}, x.options());
    int total = N * C_out * OD * OH * OW;
    const int threads = 256;
    const dim3 blocks((total + threads - 1) / threads);
    cudaStream_t stream = at::cuda::getCurrentCUDAStream();
    bool has_bias = (bias.numel() != 0);

    if (C_in == 32 && C_out == 32 && D_in == 12 && H_in == 24 && W_in == 48 &&
        KD == 3 && KH == 5 && KW == 7 && sd == 2 && sh == 2 && sw == 2 &&
        pd == 1 && ph == 2 && pw == 3 && opd == 1 && oph == 1 && opw == 1 &&
        groups == 4 && OD == 24 && OH == 48 && OW == 96) {
        if (has_bias) convt3d_s2_g4_k357_kernel<true><<<blocks, threads, 0, stream>>>(x.data_ptr<float>(), weight.data_ptr<float>(), bias.data_ptr<float>(), y.data_ptr<float>(), total);
        else convt3d_s2_g4_k357_kernel<false><<<blocks, threads, 0, stream>>>(x.data_ptr<float>(), weight.data_ptr<float>(), nullptr, y.data_ptr<float>(), total);
    } else {
        if (has_bias) convt3d_generic_kernel<true><<<blocks, threads, 0, stream>>>(x.data_ptr<float>(), weight.data_ptr<float>(), bias.data_ptr<float>(), y.data_ptr<float>(), total, N, C_in, D_in, H_in, W_in, C_out, OD, OH, OW, KD, KH, KW, sd, sh, sw, pd, ph, pw, groups);
        else convt3d_generic_kernel<false><<<blocks, threads, 0, stream>>>(x.data_ptr<float>(), weight.data_ptr<float>(), nullptr, y.data_ptr<float>(), total, N, C_in, D_in, H_in, W_in, C_out, OD, OH, OW, KD, KH, KW, sd, sh, sw, pd, ph, pw, groups);
    }
    return y;
}
'''

_ext = load_inline(
    name='convt3d_custom_ext_v1',
    cpp_sources=_cpp_src,
    cuda_sources=_cuda_src,
    functions=['conv_transpose3d_forward'],
    extra_cuda_cflags=['-O3'],
    extra_cflags=['-O3'],
    verbose=False,
)

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

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        b = self.conv_transpose3d.bias
        if b is None:
            b = torch.empty(0, device=x.device, dtype=x.dtype)
        s = self.stride; p = self.padding; op = self.output_padding
        return _ext.conv_transpose3d_forward(x, self.conv_transpose3d.weight, b,
                                             int(s[0]), int(s[1]), int(s[2]),
                                             int(p[0]), int(p[1]), int(p[2]),
                                             int(op[0]), int(op[1]), int(op[2]),
                                             int(self.groups))