gpt-5.5-priority · original · l12_default_gpt
correct 46_Average_Pooling_3D.py
model
gpt-5.5-priority
turns / max
9 / 10
tool calls
9
tokens (total)
0
wall clock
—
truncated
no
backend
cuda fp32
variant
original
runtime
8.62 μs
ref runtime
8.66 μs
speedup
1.00x
started
2026-05-02T03:05:40Z
timingkernelbench.timing
kernel runtime
8.62 μs
ref runtime
8.66 μs
speedup
1.00x
kernel mean / median / std
8.62 / — / 0.16 μs
ref mean / median / std
8.66 / — / 0.04 μs
numerical precisioneval_kernel_against_ref
max abs error
2.98e-07
mean abs error
3.44e-08
max rel error
5.62e-07
mean rel error
6.98e-08
gpu memoryextended_metrics.measure_memory
kernel peak
9216.00 MB
ref peak
9216.00 MB
kernel / ref
1.00x
kernel launches / fusiontorch.profiler
kernel launches
1
ref launches
5
fusion ratio (ref / kernel)
5.00x
total cuda time
26 μs
energyNVML (pynvml)
kernel mJ / run
3743.42
ref mJ / run
5265.56
ratio (ref / kernel)
1.41x
avg power
435.35 W
ref avg power
608.54 W
SOL (speed-of-light)nsight_hardware_counters
SOL score (0–1)
0.336
dram util %
33.6%
compute util %
19.4%
bottleneck
memory-bound
dominant pipe
FP32
arithmetic intensity
—
ridge point
20.00
roofline / occupancynsight_hardware_counters
dram bandwidth
1125.81 GB/s
dram util %
33.6%
fp32 tflops
—
fp32 util %
—
fp16 tflops
—
occupancy %
62.0%
bottleneck
memory-bound
dominant pipe %
19.4%
arithmetic intensity
—
ridge point
20.00
L1 hit %
—
L2 hit %
35.6%
ld sectors / req
5.15
st sectors / req
—
regs / thread
48
smem / block
40832 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
1679.1%
mio_throttle
185.0%
short_scoreboard
48.0%
reference PyTorch source (the prompt the agent sees)
import torch
import torch.nn as nn

class Model(nn.Module):
    """
    Simple model that performs 3D Average Pooling.
    """
    def __init__(self, kernel_size: int, stride: int = None, padding: int = 0):
        """
        Initializes the Average Pooling layer.

        Args:
            kernel_size (int): Size of the kernel to apply pooling.
            stride (int, optional): Stride of the pooling operation. Defaults to None, which uses the kernel size.
            padding (int, optional): Padding to apply before pooling. Defaults to 0.
        """
        super(Model, self).__init__()
        self.avg_pool = nn.AvgPool3d(kernel_size=kernel_size, stride=stride, padding=padding)

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        """
        Applies Average Pooling to the input tensor.

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

        Returns:
            torch.Tensor: Output tensor with Average Pooling applied, shape depends on kernel_size, stride and padding.
        """
        return self.avg_pool(x)

batch_size = 16
channels = 32
depth = 128
height = 128
width = 256
kernel_size = 3
stride = 2
padding = 1

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

def get_init_inputs():
    return [kernel_size, stride, padding]
turn 021.3s turn · 21.3s LLM · 0.0s tools
call → get_gpu_specs
callget_gpu_specs {}
result ← get_gpu_specs (ok)
okget_gpu_specs GPU specs for NVIDIA H100 80GB HBM3: total memory (runtime): 79.1 GB spec entry: H100 GPU Architecture: Hopper GPU Memory: 80GB Memory Bandwidth: 3.35 TB/s FP64 TFLOPS: 34 FP64 Tensor Core TFLOPS: 67 FP32 TFLOPS: 67 TF32 Tensor Core TFLOPS: 989 with sparsity BFLOAT16 Tensore Core TFLOPS: 1979 with sparsity FP16 Tensor Core TFLOPS: 1979 with sparsity FP8 Tensor Core TFLOPS: 3958 with sparsity INT8 Tensor Core TOPS: 3958 with sparsity Register File Size: 64K 32-bit registers per SM Maximum number of registers per thread: 255 Maximum number of thread blocks per SM: 32 Shared memory capacity per SM: 228 KB Maximum shared memory per thread block: 227 KB
turn 1 · 1 tool call(s) · 65.8s turn · 65.8s LLM · 7,355 chars (click to expand)
turn 165.8s turn · 65.8s LLM · 0.0s tools
call → compile_kernel
callcompile_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 avg_pool3d_forward(torch::Tensor x, int64_t kernel_size, 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__global__ void avgpool3d_k3s2p1_kernel(const float* __restrict__ x, float* __restrict__ y,\n int N, int C, int D, int H, int W,\n int Do, int Ho, int Wo, long long total) {\n long long idx = (long long)blockIdx.x * blockDim.x + threadIdx.x;\n long long stride_grid = (long long)blockDim.x * gridDim.x;\n const float scale = 1.0f / 27.0f;\n for (; idx < total; idx += stride_grid) {\n int ow = idx % Wo;\n long long t = idx / Wo;\n int oh = t % Ho; t /= Ho;\n int od = t % Do; t /= Do;\n int c = t % C; int n = t / C;\n int id0 = od * 2 - 1;\n int ih0 = oh * 2 - 1;\n int iw0 = ow * 2 - 1;\n float sum = 0.0f;\n if (od > 0 && oh > 0 && ow > 0) {\n long long base = (((((long long)n * C + c) * D + id0) * H + ih0) * W + iw0);\n long long plane = (long long)H * W;\n // z=0\n const float* p0 = x + base;\n sum += p0[0] + p0[1] + p0[2];\n p0 += W;\n sum += p0[0] + p0[1] + p0[2];\n p0 += W;\n sum += p0[0] + p0[1] + p0[2];\n // z=1\n p0 = x + base + plane;\n sum += p0[0] + p0[1] + p0[2];\n p0 += W;\n sum += p0[0] + p0[1] + p0[2];\n p0 += W;\n sum += p0[0] + p0[1] + p0[2];\n // z=2\n p0 = x + base + 2 * plane;\n sum += p0[0] + p0[1] + p0[2];\n p0 += W;\n sum += p0[0] + p0[1] + p0[2];\n p0 += W;\n sum += p0[0] + p0[1] + p0[2];\n } else {\n long long nc_base = (((long long)n * C + c) * D * H * W);\n #pragma unroll\n for (int dz = 0; dz < 3; ++dz) {\n int iz = id0 + dz;\n if ((unsigned)iz < (unsigned)D) {\n #pragma unroll\n for (int dy = 0; dy < 3; ++dy) {\n int iy = ih0 + dy;\n if ((unsigned)iy < (unsigned)H) {\n #pragma unroll\n for (int dx = 0; dx < 3; ++dx) {\n int ix = iw0 + dx;\n if ((unsigned)ix < (unsigned)W) {\n sum += x[nc_base + ((long long)iz * H + iy) * W + ix];\n }\n }\n }\n }\n }\n }\n }\n y[idx] = sum * scale;\n }\n}\n\n__global__ void avgpool3d_generic_kernel(const float* __restrict__ x, float* __restrict__ y,\n int N, int C, int D, int H, int W,\n int Do, int Ho, int Wo, int K, int S, int P, long long total) {\n long long idx = (long long)blockIdx.x * blockDim.x + threadIdx.x;\n long long stride_grid = (long long)blockDim.x * gridDim.x;\n float inv = 1.0f / (float)(K*K*K);\n for (; idx < total; idx += stride_grid) {\n int ow = idx % Wo;\n long long t = idx / Wo;\n int oh = t % Ho; t /= Ho;\n int od = t % Do; t /= Do;\n int c = t % C; int n = t / C;\n int id0 = od * S - P;\n int ih0 = oh * S - P;\n int iw0 = ow * S - P;\n float sum = 0.0f;\n long long nc_base = (((long long)n * C + c) * D * H * W);\n for (int dz = 0; dz < K; ++dz) {\n int iz = id0 + dz;\n if ((unsigned)iz < (unsigned)D) {\n for (int dy = 0; dy < K; ++dy) {\n int iy = ih0 + dy;\n if ((unsigned)iy < (unsigned)H) {\n for (int dx = 0; dx < K; ++dx) {\n int ix = iw0 + dx;\n if ((unsigned)ix < (unsigned)W) {\n sum += x[nc_base + ((long long)iz * H + iy) * W + ix];\n }\n }\n }\n }\n }\n }\n y[idx] = sum * inv;\n }\n}\n\ntorch::Tensor avg_pool3d_forward(torch::Tensor x, int64_t kernel_size, int64_t stride, int64_t padding) {\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.dim() == 5, \"x must be 5D\");\n auto xc = x.contiguous();\n int N = (int)xc.size(0), C = (int)xc.size(1), D = (int)xc.size(2), H = (int)xc.size(3), W = (int)xc.size(4);\n int K = (int)kernel_size;\n int S = (int)stride;\n int P = (int)padding;\n int Do = (D + 2*P - K) / S + 1;\n int Ho = (H + 2*P - K) / S + 1;\n int Wo = (W + 2*P - K) / S + 1;\n auto y = torch::empty({N, C, Do, Ho, Wo}, xc.options());\n long long total = (long long)N * C * Do * Ho * Wo;\n if (total == 0) return y;\n int threads = 256;\n int blocks = (int)((total + threads - 1) / threads);\n if (blocks > 65535) blocks = 65535;\n cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n if (K == 3 && S == 2 && P == 1) {\n avgpool3d_k3s2p1_kernel<<<blocks, threads, 0, stream>>>(xc.data_ptr<float>(), y.data_ptr<float>(), N, C, D, H, W, Do, Ho, Wo, total);\n } else {\n avgpool3d_generic_kernel<<<blocks, threads, 0, stream>>>(xc.data_ptr<float>(), y.data_ptr<float>(), N, C, D, H, W, Do, Ho, Wo, K, S, P, total);\n }\n return y;\n}\n'''\n\n_ext = load_inline(name='avgpool3d_ext_v1', cpp_sources=_cpp_src, cuda_sources=_cuda_src,\n functions=['avg_pool3d_forward'], extra_cuda_cflags=['-O3'], verbose=False)\n\nclass ModelNew(nn.Module):\n def __init__(self, kernel_size: int, stride: int = None, padding: int = 0):\n super(ModelNew, self).__init__()\n self.kernel_size = int(kernel_size)\n self.stride = int(stride) if stride is not None else int(kernel_size)\n self.padding = int(padding)\n def forward(self, x: torch.Tensor) -> torch.Tensor:\n return _ext.avg_pool3d_forward(x, self.kernel_size, self.stride, self.padding)\n" }
result ← compile_kernel (ok)
okcompile_kernel compile_kernel PASSED: kernel compiled without errors.
turn 2 · 1 tool call(s) · 12.0s turn · 12.0s LLM · 7,397 chars (click to expand)
turn 212.0s turn · 12.0s LLM · 0.0s tools
call → run_correctness
callrun_correctness { "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 avg_pool3d_forward(torch::Tensor x, int64_t kernel_size, 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__global__ void avgpool3d_k3s2p1_kernel(const float* __restrict__ x, float* __restrict__ y,\n int N, int C, int D, int H, int W,\n int Do, int Ho, int Wo, long long total) {\n long long idx = (long long)blockIdx.x * blockDim.x + threadIdx.x;\n long long stride_grid = (long long)blockDim.x * gridDim.x;\n const float scale = 1.0f / 27.0f;\n for (; idx < total; idx += stride_grid) {\n int ow = idx % Wo;\n long long t = idx / Wo;\n int oh = t % Ho; t /= Ho;\n int od = t % Do; t /= Do;\n int c = t % C; int n = t / C;\n int id0 = od * 2 - 1;\n int ih0 = oh * 2 - 1;\n int iw0 = ow * 2 - 1;\n float sum = 0.0f;\n if (od > 0 && oh > 0 && ow > 0) {\n long long base = (((((long long)n * C + c) * D + id0) * H + ih0) * W + iw0);\n long long plane = (long long)H * W;\n const float* p0 = x + base;\n sum += p0[0] + p0[1] + p0[2];\n p0 += W;\n sum += p0[0] + p0[1] + p0[2];\n p0 += W;\n sum += p0[0] + p0[1] + p0[2];\n p0 = x + base + plane;\n sum += p0[0] + p0[1] + p0[2];\n p0 += W;\n sum += p0[0] + p0[1] + p0[2];\n p0 += W;\n sum += p0[0] + p0[1] + p0[2];\n p0 = x + base + 2 * plane;\n sum += p0[0] + p0[1] + p0[2];\n p0 += W;\n sum += p0[0] + p0[1] + p0[2];\n p0 += W;\n sum += p0[0] + p0[1] + p0[2];\n } else {\n long long nc_base = (((long long)n * C + c) * D * H * W);\n #pragma unroll\n for (int dz = 0; dz < 3; ++dz) {\n int iz = id0 + dz;\n if ((unsigned)iz < (unsigned)D) {\n #pragma unroll\n for (int dy = 0; dy < 3; ++dy) {\n int iy = ih0 + dy;\n if ((unsigned)iy < (unsigned)H) {\n #pragma unroll\n for (int dx = 0; dx < 3; ++dx) {\n int ix = iw0 + dx;\n if ((unsigned)ix < (unsigned)W) {\n sum += x[nc_base + ((long long)iz * H + iy) * W + ix];\n }\n }\n }\n }\n }\n }\n }\n y[idx] = sum * scale;\n }\n}\n\n__global__ void avgpool3d_generic_kernel(const float* __restrict__ x, float* __restrict__ y,\n int N, int C, int D, int H, int W,\n int Do, int Ho, int Wo, int K, int S, int P, long long total) {\n long long idx = (long long)blockIdx.x * blockDim.x + threadIdx.x;\n long long stride_grid = (long long)blockDim.x * gridDim.x;\n float inv = 1.0f / (float)(K*K*K);\n for (; idx < total; idx += stride_grid) {\n int ow = idx % Wo;\n long long t = idx / Wo;\n int oh = t % Ho; t /= Ho;\n int od = t % Do; t /= Do;\n int c = t % C; int n = t / C;\n int id0 = od * S - P;\n int ih0 = oh * S - P;\n int iw0 = ow * S - P;\n float sum = 0.0f;\n long long nc_base = (((long long)n * C + c) * D * H * W);\n for (int dz = 0; dz < K; ++dz) {\n int iz = id0 + dz;\n if ((unsigned)iz < (unsigned)D) {\n for (int dy = 0; dy < K; ++dy) {\n int iy = ih0 + dy;\n if ((unsigned)iy < (unsigned)H) {\n for (int dx = 0; dx < K; ++dx) {\n int ix = iw0 + dx;\n if ((unsigned)ix < (unsigned)W) {\n sum += x[nc_base + ((long long)iz * H + iy) * W + ix];\n }\n }\n }\n }\n }\n }\n y[idx] = sum * inv;\n }\n}\n\ntorch::Tensor avg_pool3d_forward(torch::Tensor x, int64_t kernel_size, int64_t stride, int64_t padding) {\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.dim() == 5, \"x must be 5D\");\n auto xc = x.contiguous();\n int N = (int)xc.size(0), C = (int)xc.size(1), D = (int)xc.size(2), H = (int)xc.size(3), W = (int)xc.size(4);\n int K = (int)kernel_size;\n int S = (int)stride;\n int P = (int)padding;\n int Do = (D + 2*P - K) / S + 1;\n int Ho = (H + 2*P - K) / S + 1;\n int Wo = (W + 2*P - K) / S + 1;\n auto y = torch::empty({N, C, Do, Ho, Wo}, xc.options());\n long long total = (long long)N * C * Do * Ho * Wo;\n if (total == 0) return y;\n int threads = 256;\n int blocks = (int)((total + threads - 1) / threads);\n if (blocks > 65535) blocks = 65535;\n cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n if (K == 3 && S == 2 && P == 1) {\n avgpool3d_k3s2p1_kernel<<<blocks, threads, 0, stream>>>(xc.data_ptr<float>(), y.data_ptr<float>(), N, C, D, H, W, Do, Ho, Wo, total);\n } else {\n avgpool3d_generic_kernel<<<blocks, threads, 0, stream>>>(xc.data_ptr<float>(), y.data_ptr<float>(), N, C, D, H, W, Do, Ho, Wo, K, S, P, total);\n }\n return y;\n}\n'''\n\n_ext = load_inline(name='avgpool3d_ext_v1', cpp_sources=_cpp_src, cuda_sources=_cuda_src,\n functions=['avg_pool3d_forward'], extra_cuda_cflags=['-O3'], verbose=False)\n\nclass ModelNew(nn.Module):\n def __init__(self, kernel_size: int, stride: int = None, padding: int = 0):\n super(ModelNew, self).__init__()\n self.kernel_size = int(kernel_size)\n self.stride = int(stride) if stride is not None else int(kernel_size)\n self.padding = int(padding)\n def forward(self, x: torch.Tensor) -> torch.Tensor:\n return _ext.avg_pool3d_forward(x, self.kernel_size, self.stride, self.padding)\n" }
result ← run_correctness (ok)
okrun_correctness run_correctness PASSED: (5 / 5) trials all matched the reference. Numerical precision: max_abs_err=2.98e-07 mean_abs_err=3.37e-08 max_rel_err=5.62e-07
turn 3 · 1 tool call(s) · 44.1s turn · 44.1s LLM · 8,063 chars (click to expand)
turn 344.1s turn · 44.1s LLM · 0.0s tools
reasoning
**Considering boundary conditions** I'm evaluating the boundary for many due to the tile being 0. There's a conditional cost that's okay, but I wonder if I could have a separate fallback boundary for outputs with od/oh/ow=0. This might let the interior run faster. However, the shared tile with the conditional for tile 0 includes many valid outputs, but the bound check would only load. For the interior tile, it seems best to go direct. I should use compile.
call → compile_kernel
callcompile_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>\ntorch::Tensor avg_pool3d_forward(torch::Tensor x, int64_t kernel_size, 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 BD 4\n#define BH 8\n#define BW 32\n#define TD (2*BD+1)\n#define TH (2*BH+1)\n#define TW (2*BW+1)\n#define TSIZE (TD*TH*TW)\n\n__global__ __launch_bounds__(1024, 1) void avgpool3d_k3s2p1_shared_kernel(const float* __restrict__ x, float* __restrict__ y,\n int N, int C, int D, int H, int W,\n int Do, int Ho, int Wo) {\n __shared__ float sh[TSIZE];\n int num_w_tiles = (Wo + BW - 1) / BW;\n int num_h_tiles = (Ho + BH - 1) / BH;\n int ht_dt = blockIdx.y;\n int ht = ht_dt % num_h_tiles;\n int dt = ht_dt / num_h_tiles;\n int ow_base = blockIdx.x * BW;\n int oh_base = ht * BH;\n int od_base = dt * BD;\n int nc = blockIdx.z;\n int n = nc / C;\n int c = nc - n * C;\n int iz0 = od_base * 2 - 1;\n int iy0 = oh_base * 2 - 1;\n int ix0 = ow_base * 2 - 1;\n long long nc_base = ((long long)nc) * D * H * W;\n bool full = (iz0 >= 0 && iy0 >= 0 && ix0 >= 0 && iz0 + TD <= D && iy0 + TH <= H && ix0 + TW <= W);\n for (int s = threadIdx.x; s < TSIZE; s += blockDim.x) {\n int lx = s % TW;\n int tmp = s / TW;\n int ly = tmp % TH;\n int lz = tmp / TH;\n int iz = iz0 + lz;\n int iy = iy0 + ly;\n int ix = ix0 + lx;\n float v = 0.0f;\n if (full || ((unsigned)iz < (unsigned)D && (unsigned)iy < (unsigned)H && (unsigned)ix < (unsigned)W)) {\n v = x[nc_base + ((long long)iz * H + iy) * W + ix];\n }\n sh[s] = v;\n }\n __syncthreads();\n\n int tid = threadIdx.x;\n int lw = tid % BW;\n int tmp = tid / BW;\n int lh = tmp % BH;\n int ld = tmp / BH;\n int ow = ow_base + lw;\n int oh = oh_base + lh;\n int od = od_base + ld;\n if (od < Do && oh < Ho && ow < Wo) {\n int so = ((ld * 2) * TH + (lh * 2)) * TW + (lw * 2);\n float sum = 0.0f;\n const float* p = sh + so;\n sum += p[0] + p[1] + p[2];\n p += TW;\n sum += p[0] + p[1] + p[2];\n p += TW;\n sum += p[0] + p[1] + p[2];\n p = sh + so + TH * TW;\n sum += p[0] + p[1] + p[2];\n p += TW;\n sum += p[0] + p[1] + p[2];\n p += TW;\n sum += p[0] + p[1] + p[2];\n p = sh + so + 2 * TH * TW;\n sum += p[0] + p[1] + p[2];\n p += TW;\n sum += p[0] + p[1] + p[2];\n p += TW;\n sum += p[0] + p[1] + p[2];\n long long out = (((long long)nc * Do + od) * Ho + oh) * Wo + ow;\n y[out] = sum * (1.0f / 27.0f);\n }\n}\n\n__global__ void avgpool3d_generic_kernel(const float* __restrict__ x, float* __restrict__ y,\n int N, int C, int D, int H, int W,\n int Do, int Ho, int Wo, int K, int S, int P, long long total) {\n long long idx = (long long)blockIdx.x * blockDim.x + threadIdx.x;\n long long stride_grid = (long long)blockDim.x * gridDim.x;\n float inv = 1.0f / (float)(K*K*K);\n for (; idx < total; idx += stride_grid) {\n int ow = idx % Wo;\n long long t = idx / Wo;\n int oh = t % Ho; t /= Ho;\n int od = t % Do; t /= Do;\n int c = t % C; int n = t / C;\n int id0 = od * S - P;\n int ih0 = oh * S - P;\n int iw0 = ow * S - P;\n float sum = 0.0f;\n long long nc_base = (((long long)n * C + c) * D * H * W);\n for (int dz = 0; dz < K; ++dz) {\n int iz = id0 + dz;\n if ((unsigned)iz < (unsigned)D) {\n for (int dy = 0; dy < K; ++dy) {\n int iy = ih0 + dy;\n if ((unsigned)iy < (unsigned)H) {\n for (int dx = 0; dx < K; ++dx) {\n int ix = iw0 + dx;\n if ((unsigned)ix < (unsigned)W) {\n sum += x[nc_base + ((long long)iz * H + iy) * W + ix];\n }\n }\n }\n }\n }\n }\n y[idx] = sum * inv;\n }\n}\n\ntorch::Tensor avg_pool3d_forward(torch::Tensor x, int64_t kernel_size, int64_t stride, int64_t padding) {\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.dim() == 5, \"x must be 5D\");\n auto xc = x.contiguous();\n int N = (int)xc.size(0), C = (int)xc.size(1), D = (int)xc.size(2), H = (int)xc.size(3), W = (int)xc.size(4);\n int K = (int)kernel_size;\n int S = (int)stride;\n int P = (int)padding;\n int Do = (D + 2*P - K) / S + 1;\n int Ho = (H + 2*P - K) / S + 1;\n int Wo = (W + 2*P - K) / S + 1;\n auto y = torch::empty({N, C, Do, Ho, Wo}, xc.options());\n long long total = (long long)N * C * Do * Ho * Wo;\n if (total == 0) return y;\n cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n if (K == 3 && S == 2 && P == 1) {\n dim3 block(1024);\n dim3 grid((Wo + BW - 1) / BW, ((Ho + BH - 1) / BH) * ((Do + BD - 1) / BD), N * C);\n avgpool3d_k3s2p1_shared_kernel<<<grid, block, 0, stream>>>(xc.data_ptr<float>(), y.data_ptr<float>(), N, C, D, H, W, Do, Ho, Wo);\n } else {\n int threads = 256;\n int blocks = (int)((total + threads - 1) / threads);\n if (blocks > 65535) blocks = 65535;\n avgpool3d_generic_kernel<<<blocks, threads, 0, stream>>>(xc.data_ptr<float>(), y.data_ptr<float>(), N, C, D, H, W, Do, Ho, Wo, K, S, P, total);\n }\n return y;\n}\n'''\n\n_ext = load_inline(name='avgpool3d_ext_v2', cpp_sources=_cpp_src, cuda_sources=_cuda_src,\n functions=['avg_pool3d_forward'], extra_cuda_cflags=['-O3'], verbose=False)\n\nclass ModelNew(nn.Module):\n def __init__(self, kernel_size: int, stride: int = None, padding: int = 0):\n super(ModelNew, self).__init__()\n self.kernel_size = int(kernel_size)\n self.stride = int(stride) if stride is not None else int(kernel_size)\n self.padding = int(padding)\n def forward(self, x: torch.Tensor) -> torch.Tensor:\n return _ext.avg_pool3d_forward(x, self.kernel_size, self.stride, self.padding)\n" }
result ← compile_kernel (ok)
okcompile_kernel compile_kernel PASSED: kernel compiled without errors.
turn 4 · 1 tool call(s) · 15.5s turn · 15.5s LLM · 7,500 chars (click to expand)
turn 415.5s turn · 15.5s LLM · 0.0s tools
call → run_correctness
callrun_correctness { "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>\ntorch::Tensor avg_pool3d_forward(torch::Tensor x, int64_t kernel_size, 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#define BD 4\n#define BH 8\n#define BW 32\n#define TD (2*BD+1)\n#define TH (2*BH+1)\n#define TW (2*BW+1)\n#define TSIZE (TD*TH*TW)\n__global__ __launch_bounds__(1024, 1) void avgpool3d_k3s2p1_shared_kernel(const float* __restrict__ x, float* __restrict__ y,\n int N, int C, int D, int H, int W,\n int Do, int Ho, int Wo) {\n __shared__ float sh[TSIZE];\n int num_h_tiles = (Ho + BH - 1) / BH;\n int ht_dt = blockIdx.y;\n int ht = ht_dt % num_h_tiles;\n int dt = ht_dt / num_h_tiles;\n int ow_base = blockIdx.x * BW;\n int oh_base = ht * BH;\n int od_base = dt * BD;\n int nc = blockIdx.z;\n int iz0 = od_base * 2 - 1;\n int iy0 = oh_base * 2 - 1;\n int ix0 = ow_base * 2 - 1;\n long long nc_base = ((long long)nc) * D * H * W;\n bool full = (iz0 >= 0 && iy0 >= 0 && ix0 >= 0 && iz0 + TD <= D && iy0 + TH <= H && ix0 + TW <= W);\n for (int s = threadIdx.x; s < TSIZE; s += blockDim.x) {\n int lx = s % TW;\n int tmp = s / TW;\n int ly = tmp % TH;\n int lz = tmp / TH;\n int iz = iz0 + lz;\n int iy = iy0 + ly;\n int ix = ix0 + lx;\n float v = 0.0f;\n if (full || ((unsigned)iz < (unsigned)D && (unsigned)iy < (unsigned)H && (unsigned)ix < (unsigned)W)) {\n v = x[nc_base + ((long long)iz * H + iy) * W + ix];\n }\n sh[s] = v;\n }\n __syncthreads();\n int tid = threadIdx.x;\n int lw = tid % BW;\n int tmp = tid / BW;\n int lh = tmp % BH;\n int ld = tmp / BH;\n int ow = ow_base + lw;\n int oh = oh_base + lh;\n int od = od_base + ld;\n if (od < Do && oh < Ho && ow < Wo) {\n int so = ((ld * 2) * TH + (lh * 2)) * TW + (lw * 2);\n float sum = 0.0f;\n const float* p = sh + so;\n sum += p[0] + p[1] + p[2];\n p += TW;\n sum += p[0] + p[1] + p[2];\n p += TW;\n sum += p[0] + p[1] + p[2];\n p = sh + so + TH * TW;\n sum += p[0] + p[1] + p[2];\n p += TW;\n sum += p[0] + p[1] + p[2];\n p += TW;\n sum += p[0] + p[1] + p[2];\n p = sh + so + 2 * TH * TW;\n sum += p[0] + p[1] + p[2];\n p += TW;\n sum += p[0] + p[1] + p[2];\n p += TW;\n sum += p[0] + p[1] + p[2];\n long long out = (((long long)nc * Do + od) * Ho + oh) * Wo + ow;\n y[out] = sum * (1.0f / 27.0f);\n }\n}\n__global__ void avgpool3d_generic_kernel(const float* __restrict__ x, float* __restrict__ y,\n int N, int C, int D, int H, int W,\n int Do, int Ho, int Wo, int K, int S, int P, long long total) {\n long long idx = (long long)blockIdx.x * blockDim.x + threadIdx.x;\n long long stride_grid = (long long)blockDim.x * gridDim.x;\n float inv = 1.0f / (float)(K*K*K);\n for (; idx < total; idx += stride_grid) {\n int ow = idx % Wo;\n long long t = idx / Wo;\n int oh = t % Ho; t /= Ho;\n int od = t % Do; t /= Do;\n int c = t % C; int n = t / C;\n int id0 = od * S - P;\n int ih0 = oh * S - P;\n int iw0 = ow * S - P;\n float sum = 0.0f;\n long long nc_base = (((long long)n * C + c) * D * H * W);\n for (int dz = 0; dz < K; ++dz) {\n int iz = id0 + dz;\n if ((unsigned)iz < (unsigned)D) {\n for (int dy = 0; dy < K; ++dy) {\n int iy = ih0 + dy;\n if ((unsigned)iy < (unsigned)H) {\n for (int dx = 0; dx < K; ++dx) {\n int ix = iw0 + dx;\n if ((unsigned)ix < (unsigned)W) {\n sum += x[nc_base + ((long long)iz * H + iy) * W + ix];\n }\n }\n }\n }\n }\n }\n y[idx] = sum * inv;\n }\n}\ntorch::Tensor avg_pool3d_forward(torch::Tensor x, int64_t kernel_size, int64_t stride, int64_t padding) {\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.dim() == 5, \"x must be 5D\");\n auto xc = x.contiguous();\n int N = (int)xc.size(0), C = (int)xc.size(1), D = (int)xc.size(2), H = (int)xc.size(3), W = (int)xc.size(4);\n int K = (int)kernel_size;\n int S = (int)stride;\n int P = (int)padding;\n int Do = (D + 2*P - K) / S + 1;\n int Ho = (H + 2*P - K) / S + 1;\n int Wo = (W + 2*P - K) / S + 1;\n auto y = torch::empty({N, C, Do, Ho, Wo}, xc.options());\n long long total = (long long)N * C * Do * Ho * Wo;\n if (total == 0) return y;\n cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n if (K == 3 && S == 2 && P == 1) {\n dim3 block(1024);\n dim3 grid((Wo + BW - 1) / BW, ((Ho + BH - 1) / BH) * ((Do + BD - 1) / BD), N * C);\n avgpool3d_k3s2p1_shared_kernel<<<grid, block, 0, stream>>>(xc.data_ptr<float>(), y.data_ptr<float>(), N, C, D, H, W, Do, Ho, Wo);\n } else {\n int threads = 256;\n int blocks = (int)((total + threads - 1) / threads);\n if (blocks > 65535) blocks = 65535;\n avgpool3d_generic_kernel<<<blocks, threads, 0, stream>>>(xc.data_ptr<float>(), y.data_ptr<float>(), N, C, D, H, W, Do, Ho, Wo, K, S, P, total);\n }\n return y;\n}\n'''\n\n_ext = load_inline(name='avgpool3d_ext_v2', cpp_sources=_cpp_src, cuda_sources=_cuda_src,\n functions=['avg_pool3d_forward'], extra_cuda_cflags=['-O3'], verbose=False)\nclass ModelNew(nn.Module):\n def __init__(self, kernel_size: int, stride: int = None, padding: int = 0):\n super(ModelNew, self).__init__()\n self.kernel_size = int(kernel_size)\n self.stride = int(stride) if stride is not None else int(kernel_size)\n self.padding = int(padding)\n def forward(self, x: torch.Tensor) -> torch.Tensor:\n return _ext.avg_pool3d_forward(x, self.kernel_size, self.stride, self.padding)\n" }
result ← run_correctness (ok)
okrun_correctness run_correctness PASSED: (5 / 5) trials all matched the reference. Numerical precision: max_abs_err=2.98e-07 mean_abs_err=3.44e-08 max_rel_err=5.62e-07
turn 5 · 1 tool call(s) · 65.4s turn · 65.4s LLM · 8,849 chars (click to expand)
turn 565.4s turn · 65.4s LLM · 0.0s tools
reasoning
**Analyzing stride and offsets** I'm considering even strides for consecutive lw (length widths). It seems with offsets of 2*lw, they still remain even. This is essential because the outputs will stride by 2. Keeping this pattern consistent is important for generating the expected results, and it ensures continuity throughout the process. It looks like I should reaffirm the calculations maintaining this consistency in offsets, to verify they align with the overall structure expected in the outputs. **Considering optimization methods** I'm thinking that keeping things direct might simplify the process. I'm wondering if I could get timing by using submit, but then I think that might not work. Should I include both kernels and select? But it seems there's no runtime timing involved. I'm looking into whether a simpler option could work better for large fixed sizes. PyTorch's AvgPool3d might offer efficiency with one thread output, but it'll depend on the context and shared resources.
call → compile_kernel
callcompile_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>\ntorch::Tensor avg_pool3d_forward(torch::Tensor x, int64_t kernel_size, 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#define BD 4\n#define BH 8\n#define BW 32\n#define TD (2*BD+1)\n#define TH (2*BH+1)\n#define TW (2*BW+1)\n#define TSIZE (TD*TH*TW)\n\n__global__ __launch_bounds__(256, 4) void avgpool3d_k3s2p1_shared4_kernel(const float* __restrict__ x, float* __restrict__ y,\n int D, int H, int W, int Do, int Ho, int Wo, int C) {\n __shared__ float sh[TSIZE];\n int num_h_tiles = (Ho + BH - 1) / BH;\n int ht_dt = blockIdx.y;\n int ht = ht_dt % num_h_tiles;\n int dt = ht_dt / num_h_tiles;\n int ow_base = blockIdx.x * BW;\n int oh_base = ht * BH;\n int od_base = dt * BD;\n int nc = blockIdx.z;\n int iz0 = od_base * 2 - 1;\n int iy0 = oh_base * 2 - 1;\n int ix0 = ow_base * 2 - 1;\n long long nc_base = ((long long)nc) * D * H * W;\n bool full = (iz0 >= 0 && iy0 >= 0 && ix0 >= 0 && iz0 + TD <= D && iy0 + TH <= H && ix0 + TW <= W);\n for (int s = threadIdx.x; s < TSIZE; s += blockDim.x) {\n int lx = s % TW;\n int tmp = s / TW;\n int ly = tmp % TH;\n int lz = tmp / TH;\n int iz = iz0 + lz;\n int iy = iy0 + ly;\n int ix = ix0 + lx;\n float v = 0.0f;\n if (full || ((unsigned)iz < (unsigned)D && (unsigned)iy < (unsigned)H && (unsigned)ix < (unsigned)W)) {\n v = x[nc_base + ((long long)iz * H + iy) * W + ix];\n }\n sh[s] = v;\n }\n __syncthreads();\n\n int tid = threadIdx.x;\n int gw = tid & 7; // 0..7, four outputs each\n int tmp = tid >> 3;\n int lh = tmp & 7; // 0..7\n int ld = tmp >> 3; // 0..3\n int ow = ow_base + (gw << 2);\n int oh = oh_base + lh;\n int od = od_base + ld;\n if (od < Do && oh < Ho && ow < Wo) {\n int sx = gw << 3; // input/shared x offset = 2*local output w\n int sy = lh << 1;\n int sz = ld << 1;\n float s0=0.0f, s1=0.0f, s2=0.0f, s3=0.0f;\n #pragma unroll\n for (int dz = 0; dz < 3; ++dz) {\n #pragma unroll\n for (int dy = 0; dy < 3; ++dy) {\n const float* p = sh + ((sz + dz) * TH + (sy + dy)) * TW + sx;\n float a0=p[0], a1=p[1], a2=p[2], a3=p[3], a4=p[4], a5=p[5], a6=p[6], a7=p[7], a8=p[8];\n s0 += a0 + a1 + a2;\n s1 += a2 + a3 + a4;\n s2 += a4 + a5 + a6;\n s3 += a6 + a7 + a8;\n }\n }\n long long out = (((long long)nc * Do + od) * Ho + oh) * Wo + ow;\n const float scale = 1.0f / 27.0f;\n y[out] = s0 * scale;\n if (ow + 1 < Wo) y[out + 1] = s1 * scale;\n if (ow + 2 < Wo) y[out + 2] = s2 * scale;\n if (ow + 3 < Wo) y[out + 3] = s3 * scale;\n }\n}\n\n__global__ void avgpool3d_generic_kernel(const float* __restrict__ x, float* __restrict__ y,\n int N, int C, int D, int H, int W,\n int Do, int Ho, int Wo, int K, int S, int P, long long total) {\n long long idx = (long long)blockIdx.x * blockDim.x + threadIdx.x;\n long long stride_grid = (long long)blockDim.x * gridDim.x;\n float inv = 1.0f / (float)(K*K*K);\n for (; idx < total; idx += stride_grid) {\n int ow = idx % Wo;\n long long t = idx / Wo;\n int oh = t % Ho; t /= Ho;\n int od = t % Do; t /= Do;\n int c = t % C; int n = t / C;\n int id0 = od * S - P;\n int ih0 = oh * S - P;\n int iw0 = ow * S - P;\n float sum = 0.0f;\n long long nc_base = (((long long)n * C + c) * D * H * W);\n for (int dz = 0; dz < K; ++dz) {\n int iz = id0 + dz;\n if ((unsigned)iz < (unsigned)D) {\n for (int dy = 0; dy < K; ++dy) {\n int iy = ih0 + dy;\n if ((unsigned)iy < (unsigned)H) {\n for (int dx = 0; dx < K; ++dx) {\n int ix = iw0 + dx;\n if ((unsigned)ix < (unsigned)W) {\n sum += x[nc_base + ((long long)iz * H + iy) * W + ix];\n }\n }\n }\n }\n }\n }\n y[idx] = sum * inv;\n }\n}\n\ntorch::Tensor avg_pool3d_forward(torch::Tensor x, int64_t kernel_size, int64_t stride, int64_t padding) {\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.dim() == 5, \"x must be 5D\");\n auto xc = x.contiguous();\n int N = (int)xc.size(0), C = (int)xc.size(1), D = (int)xc.size(2), H = (int)xc.size(3), W = (int)xc.size(4);\n int K = (int)kernel_size;\n int S = (int)stride;\n int P = (int)padding;\n int Do = (D + 2*P - K) / S + 1;\n int Ho = (H + 2*P - K) / S + 1;\n int Wo = (W + 2*P - K) / S + 1;\n auto y = torch::empty({N, C, Do, Ho, Wo}, xc.options());\n long long total = (long long)N * C * Do * Ho * Wo;\n if (total == 0) return y;\n cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n if (K == 3 && S == 2 && P == 1) {\n dim3 block(256);\n dim3 grid((Wo + BW - 1) / BW, ((Ho + BH - 1) / BH) * ((Do + BD - 1) / BD), N * C);\n avgpool3d_k3s2p1_shared4_kernel<<<grid, block, 0, stream>>>(xc.data_ptr<float>(), y.data_ptr<float>(), D, H, W, Do, Ho, Wo, C);\n } else {\n int threads = 256;\n int blocks = (int)((total + threads - 1) / threads);\n if (blocks > 65535) blocks = 65535;\n avgpool3d_generic_kernel<<<blocks, threads, 0, stream>>>(xc.data_ptr<float>(), y.data_ptr<float>(), N, C, D, H, W, Do, Ho, Wo, K, S, P, total);\n }\n return y;\n}\n'''\n\n_ext = load_inline(name='avgpool3d_ext_v3', cpp_sources=_cpp_src, cuda_sources=_cuda_src,\n functions=['avg_pool3d_forward'], extra_cuda_cflags=['-O3'], verbose=False)\nclass ModelNew(nn.Module):\n def __init__(self, kernel_size: int, stride: int = None, padding: int = 0):\n super(ModelNew, self).__init__()\n self.kernel_size = int(kernel_size)\n self.stride = int(stride) if stride is not None else int(kernel_size)\n self.padding = int(padding)\n def forward(self, x: torch.Tensor) -> torch.Tensor:\n return _ext.avg_pool3d_forward(x, self.kernel_size, self.stride, self.padding)\n" }
result ← compile_kernel (ok)
okcompile_kernel compile_kernel PASSED: kernel compiled without errors.
turn 6 · 1 tool call(s) · 14.3s turn · 14.3s LLM · 7,702 chars (click to expand)
turn 614.3s turn · 14.3s LLM · 0.0s tools
call → run_correctness
callrun_correctness { "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>\ntorch::Tensor avg_pool3d_forward(torch::Tensor x, int64_t kernel_size, 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#define BD 4\n#define BH 8\n#define BW 32\n#define TD (2*BD+1)\n#define TH (2*BH+1)\n#define TW (2*BW+1)\n#define TSIZE (TD*TH*TW)\n__global__ __launch_bounds__(256, 4) void avgpool3d_k3s2p1_shared4_kernel(const float* __restrict__ x, float* __restrict__ y,\n int D, int H, int W, int Do, int Ho, int Wo, int C) {\n __shared__ float sh[TSIZE];\n int num_h_tiles = (Ho + BH - 1) / BH;\n int ht_dt = blockIdx.y;\n int ht = ht_dt % num_h_tiles;\n int dt = ht_dt / num_h_tiles;\n int ow_base = blockIdx.x * BW;\n int oh_base = ht * BH;\n int od_base = dt * BD;\n int nc = blockIdx.z;\n int iz0 = od_base * 2 - 1;\n int iy0 = oh_base * 2 - 1;\n int ix0 = ow_base * 2 - 1;\n long long nc_base = ((long long)nc) * D * H * W;\n bool full = (iz0 >= 0 && iy0 >= 0 && ix0 >= 0 && iz0 + TD <= D && iy0 + TH <= H && ix0 + TW <= W);\n for (int s = threadIdx.x; s < TSIZE; s += blockDim.x) {\n int lx = s % TW;\n int tmp = s / TW;\n int ly = tmp % TH;\n int lz = tmp / TH;\n int iz = iz0 + lz;\n int iy = iy0 + ly;\n int ix = ix0 + lx;\n float v = 0.0f;\n if (full || ((unsigned)iz < (unsigned)D && (unsigned)iy < (unsigned)H && (unsigned)ix < (unsigned)W)) {\n v = x[nc_base + ((long long)iz * H + iy) * W + ix];\n }\n sh[s] = v;\n }\n __syncthreads();\n int tid = threadIdx.x;\n int gw = tid & 7;\n int tmp = tid >> 3;\n int lh = tmp & 7;\n int ld = tmp >> 3;\n int ow = ow_base + (gw << 2);\n int oh = oh_base + lh;\n int od = od_base + ld;\n if (od < Do && oh < Ho && ow < Wo) {\n int sx = gw << 3;\n int sy = lh << 1;\n int sz = ld << 1;\n float s0=0.0f, s1=0.0f, s2=0.0f, s3=0.0f;\n #pragma unroll\n for (int dz = 0; dz < 3; ++dz) {\n #pragma unroll\n for (int dy = 0; dy < 3; ++dy) {\n const float* p = sh + ((sz + dz) * TH + (sy + dy)) * TW + sx;\n float a0=p[0], a1=p[1], a2=p[2], a3=p[3], a4=p[4], a5=p[5], a6=p[6], a7=p[7], a8=p[8];\n s0 += a0 + a1 + a2;\n s1 += a2 + a3 + a4;\n s2 += a4 + a5 + a6;\n s3 += a6 + a7 + a8;\n }\n }\n long long out = (((long long)nc * Do + od) * Ho + oh) * Wo + ow;\n const float scale = 1.0f / 27.0f;\n y[out] = s0 * scale;\n if (ow + 1 < Wo) y[out + 1] = s1 * scale;\n if (ow + 2 < Wo) y[out + 2] = s2 * scale;\n if (ow + 3 < Wo) y[out + 3] = s3 * scale;\n }\n}\n__global__ void avgpool3d_generic_kernel(const float* __restrict__ x, float* __restrict__ y,\n int N, int C, int D, int H, int W,\n int Do, int Ho, int Wo, int K, int S, int P, long long total) {\n long long idx = (long long)blockIdx.x * blockDim.x + threadIdx.x;\n long long stride_grid = (long long)blockDim.x * gridDim.x;\n float inv = 1.0f / (float)(K*K*K);\n for (; idx < total; idx += stride_grid) {\n int ow = idx % Wo;\n long long t = idx / Wo;\n int oh = t % Ho; t /= Ho;\n int od = t % Do; t /= Do;\n int c = t % C; int n = t / C;\n int id0 = od * S - P;\n int ih0 = oh * S - P;\n int iw0 = ow * S - P;\n float sum = 0.0f;\n long long nc_base = (((long long)n * C + c) * D * H * W);\n for (int dz = 0; dz < K; ++dz) {\n int iz = id0 + dz;\n if ((unsigned)iz < (unsigned)D) {\n for (int dy = 0; dy < K; ++dy) {\n int iy = ih0 + dy;\n if ((unsigned)iy < (unsigned)H) {\n for (int dx = 0; dx < K; ++dx) {\n int ix = iw0 + dx;\n if ((unsigned)ix < (unsigned)W) {\n sum += x[nc_base + ((long long)iz * H + iy) * W + ix];\n }\n }\n }\n }\n }\n }\n y[idx] = sum * inv;\n }\n}\ntorch::Tensor avg_pool3d_forward(torch::Tensor x, int64_t kernel_size, int64_t stride, int64_t padding) {\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.dim() == 5, \"x must be 5D\");\n auto xc = x.contiguous();\n int N = (int)xc.size(0), C = (int)xc.size(1), D = (int)xc.size(2), H = (int)xc.size(3), W = (int)xc.size(4);\n int K = (int)kernel_size;\n int S = (int)stride;\n int P = (int)padding;\n int Do = (D + 2*P - K) / S + 1;\n int Ho = (H + 2*P - K) / S + 1;\n int Wo = (W + 2*P - K) / S + 1;\n auto y = torch::empty({N, C, Do, Ho, Wo}, xc.options());\n long long total = (long long)N * C * Do * Ho * Wo;\n if (total == 0) return y;\n cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n if (K == 3 && S == 2 && P == 1) {\n dim3 block(256);\n dim3 grid((Wo + BW - 1) / BW, ((Ho + BH - 1) / BH) * ((Do + BD - 1) / BD), N * C);\n avgpool3d_k3s2p1_shared4_kernel<<<grid, block, 0, stream>>>(xc.data_ptr<float>(), y.data_ptr<float>(), D, H, W, Do, Ho, Wo, C);\n } else {\n int threads = 256;\n int blocks = (int)((total + threads - 1) / threads);\n if (blocks > 65535) blocks = 65535;\n avgpool3d_generic_kernel<<<blocks, threads, 0, stream>>>(xc.data_ptr<float>(), y.data_ptr<float>(), N, C, D, H, W, Do, Ho, Wo, K, S, P, total);\n }\n return y;\n}\n'''\n_ext = load_inline(name='avgpool3d_ext_v3', cpp_sources=_cpp_src, cuda_sources=_cuda_src,\n functions=['avg_pool3d_forward'], extra_cuda_cflags=['-O3'], verbose=False)\nclass ModelNew(nn.Module):\n def __init__(self, kernel_size: int, stride: int = None, padding: int = 0):\n super(ModelNew, self).__init__()\n self.kernel_size = int(kernel_size)\n self.stride = int(stride) if stride is not None else int(kernel_size)\n self.padding = int(padding)\n def forward(self, x: torch.Tensor) -> torch.Tensor:\n return _ext.avg_pool3d_forward(x, self.kernel_size, self.stride, self.padding)\n" }
result ← run_correctness (ok)
okrun_correctness run_correctness PASSED: (5 / 5) trials all matched the reference. Numerical precision: max_abs_err=2.98e-07 mean_abs_err=3.44e-08 max_rel_err=5.62e-07
turn 7 · 1 tool call(s) · 20.3s turn · 20.3s LLM · 7,594 chars (click to expand)
turn 720.3s turn · 20.3s LLM · 0.0s tools
call → static_check
callstatic_check { "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>\ntorch::Tensor avg_pool3d_forward(torch::Tensor x, int64_t kernel_size, 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#define BD 4\n#define BH 8\n#define BW 32\n#define TD (2*BD+1)\n#define TH (2*BH+1)\n#define TW (2*BW+1)\n#define TSIZE (TD*TH*TW)\n__global__ __launch_bounds__(256, 4) void avgpool3d_k3s2p1_shared4_kernel(const float* __restrict__ x, float* __restrict__ y,\n int D, int H, int W, int Do, int Ho, int Wo, int C) {\n __shared__ float sh[TSIZE];\n int num_h_tiles = (Ho + BH - 1) / BH;\n int ht_dt = blockIdx.y;\n int ht = ht_dt % num_h_tiles;\n int dt = ht_dt / num_h_tiles;\n int ow_base = blockIdx.x * BW;\n int oh_base = ht * BH;\n int od_base = dt * BD;\n int nc = blockIdx.z;\n int iz0 = od_base * 2 - 1;\n int iy0 = oh_base * 2 - 1;\n int ix0 = ow_base * 2 - 1;\n long long nc_base = ((long long)nc) * D * H * W;\n bool full = (iz0 >= 0 && iy0 >= 0 && ix0 >= 0 && iz0 + TD <= D && iy0 + TH <= H && ix0 + TW <= W);\n for (int s = threadIdx.x; s < TSIZE; s += blockDim.x) {\n int lx = s % TW;\n int tmp = s / TW;\n int ly = tmp % TH;\n int lz = tmp / TH;\n int iz = iz0 + lz;\n int iy = iy0 + ly;\n int ix = ix0 + lx;\n float v = 0.0f;\n if (full || ((unsigned)iz < (unsigned)D && (unsigned)iy < (unsigned)H && (unsigned)ix < (unsigned)W)) {\n v = x[nc_base + ((long long)iz * H + iy) * W + ix];\n }\n sh[s] = v;\n }\n __syncthreads();\n int tid = threadIdx.x;\n int gw = tid & 7;\n int tmp = tid >> 3;\n int lh = tmp & 7;\n int ld = tmp >> 3;\n int ow = ow_base + (gw << 2);\n int oh = oh_base + lh;\n int od = od_base + ld;\n if (od < Do && oh < Ho && ow < Wo) {\n int sx = gw << 3;\n int sy = lh << 1;\n int sz = ld << 1;\n float s0=0.0f, s1=0.0f, s2=0.0f, s3=0.0f;\n #pragma unroll\n for (int dz = 0; dz < 3; ++dz) {\n #pragma unroll\n for (int dy = 0; dy < 3; ++dy) {\n const float* p = sh + ((sz + dz) * TH + (sy + dy)) * TW + sx;\n float a0=p[0], a1=p[1], a2=p[2], a3=p[3], a4=p[4], a5=p[5], a6=p[6], a7=p[7], a8=p[8];\n s0 += a0 + a1 + a2;\n s1 += a2 + a3 + a4;\n s2 += a4 + a5 + a6;\n s3 += a6 + a7 + a8;\n }\n }\n long long out = (((long long)nc * Do + od) * Ho + oh) * Wo + ow;\n const float scale = 1.0f / 27.0f;\n y[out] = s0 * scale;\n if (ow + 1 < Wo) y[out + 1] = s1 * scale;\n if (ow + 2 < Wo) y[out + 2] = s2 * scale;\n if (ow + 3 < Wo) y[out + 3] = s3 * scale;\n }\n}\n__global__ void avgpool3d_generic_kernel(const float* __restrict__ x, float* __restrict__ y,\n int N, int C, int D, int H, int W,\n int Do, int Ho, int Wo, int K, int S, int P, long long total) {\n long long idx = (long long)blockIdx.x * blockDim.x + threadIdx.x;\n long long stride_grid = (long long)blockDim.x * gridDim.x;\n float inv = 1.0f / (float)(K*K*K);\n for (; idx < total; idx += stride_grid) {\n int ow = idx % Wo;\n long long t = idx / Wo;\n int oh = t % Ho; t /= Ho;\n int od = t % Do; t /= Do;\n int c = t % C; int n = t / C;\n int id0 = od * S - P;\n int ih0 = oh * S - P;\n int iw0 = ow * S - P;\n float sum = 0.0f;\n long long nc_base = (((long long)n * C + c) * D * H * W);\n for (int dz = 0; dz < K; ++dz) {\n int iz = id0 + dz;\n if ((unsigned)iz < (unsigned)D) {\n for (int dy = 0; dy < K; ++dy) {\n int iy = ih0 + dy;\n if ((unsigned)iy < (unsigned)H) {\n for (int dx = 0; dx < K; ++dx) {\n int ix = iw0 + dx;\n if ((unsigned)ix < (unsigned)W) {\n sum += x[nc_base + ((long long)iz * H + iy) * W + ix];\n }\n }\n }\n }\n }\n }\n y[idx] = sum * inv;\n }\n}\ntorch::Tensor avg_pool3d_forward(torch::Tensor x, int64_t kernel_size, int64_t stride, int64_t padding) {\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.dim() == 5, \"x must be 5D\");\n auto xc = x.contiguous();\n int N = (int)xc.size(0), C = (int)xc.size(1), D = (int)xc.size(2), H = (int)xc.size(3), W = (int)xc.size(4);\n int K = (int)kernel_size;\n int S = (int)stride;\n int P = (int)padding;\n int Do = (D + 2*P - K) / S + 1;\n int Ho = (H + 2*P - K) / S + 1;\n int Wo = (W + 2*P - K) / S + 1;\n auto y = torch::empty({N, C, Do, Ho, Wo}, xc.options());\n long long total = (long long)N * C * Do * Ho * Wo;\n if (total == 0) return y;\n cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n if (K == 3 && S == 2 && P == 1) {\n dim3 block(256);\n dim3 grid((Wo + BW - 1) / BW, ((Ho + BH - 1) / BH) * ((Do + BD - 1) / BD), N * C);\n avgpool3d_k3s2p1_shared4_kernel<<<grid, block, 0, stream>>>(xc.data_ptr<float>(), y.data_ptr<float>(), D, H, W, Do, Ho, Wo, C);\n } else {\n int threads = 256;\n int blocks = (int)((total + threads - 1) / threads);\n if (blocks > 65535) blocks = 65535;\n avgpool3d_generic_kernel<<<blocks, threads, 0, stream>>>(xc.data_ptr<float>(), y.data_ptr<float>(), N, C, D, H, W, Do, Ho, Wo, K, S, P, total);\n }\n return y;\n}\n'''\n_ext = load_inline(name='avgpool3d_ext_v3', cpp_sources=_cpp_src, cuda_sources=_cuda_src,\n functions=['avg_pool3d_forward'], extra_cuda_cflags=['-O3'], verbose=False)\nclass ModelNew(nn.Module):\n def __init__(self, kernel_size: int, stride: int = None, padding: int = 0):\n super(ModelNew, self).__init__()\n self.kernel_size = int(kernel_size)\n self.stride = int(stride) if stride is not None else int(kernel_size)\n self.padding = int(padding)\n def forward(self, x: torch.Tensor) -> torch.Tensor:\n return _ext.avg_pool3d_forward(x, self.kernel_size, self.stride, self.padding)\n" }
result ← static_check (ok)
okstatic_check static_check PASSED: no violations or warnings detected.
turn 8 · FINAL · 1 tool call(s) · 19.6s turn · 19.6s LLM · 8,712 chars (click to expand)
turn 819.6s turn · 19.6s LLM · 0.0s tools · FINAL
reasoning
**Deciding on submission method** I need to submit the final version, and I'm considering using v3. However, I'm unsure about the timing. I might test submitting only the ends first. I also need to provide the code, and I wonder if v3 shared is actually faster than v1. I really want to be confident in this decision, so there's a possibility that I could use the submit option. Let's see how this plays out!
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>\ntorch::Tensor avg_pool3d_forward(torch::Tensor x, int64_t kernel_size, 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 BD 4\n#define BH 8\n#define BW 32\n#define TD (2*BD+1)\n#define TH (2*BH+1)\n#define TW (2*BW+1)\n#define TSIZE (TD*TH*TW)\n\n__global__ __launch_bounds__(256, 4) void avgpool3d_k3s2p1_shared4_kernel(\n const float* __restrict__ x, float* __restrict__ y,\n int D, int H, int W, int Do, int Ho, int Wo, int C) {\n __shared__ float sh[TSIZE];\n\n int num_h_tiles = (Ho + BH - 1) / BH;\n int ht_dt = blockIdx.y;\n int ht = ht_dt % num_h_tiles;\n int dt = ht_dt / num_h_tiles;\n\n int ow_base = blockIdx.x * BW;\n int oh_base = ht * BH;\n int od_base = dt * BD;\n int nc = blockIdx.z;\n\n int iz0 = od_base * 2 - 1;\n int iy0 = oh_base * 2 - 1;\n int ix0 = ow_base * 2 - 1;\n long long nc_base = ((long long)nc) * D * H * W;\n\n bool full = (iz0 >= 0 && iy0 >= 0 && ix0 >= 0 &&\n iz0 + TD <= D && iy0 + TH <= H && ix0 + TW <= W);\n\n for (int s = threadIdx.x; s < TSIZE; s += blockDim.x) {\n int lx = s % TW;\n int tmp = s / TW;\n int ly = tmp % TH;\n int lz = tmp / TH;\n int iz = iz0 + lz;\n int iy = iy0 + ly;\n int ix = ix0 + lx;\n float v = 0.0f;\n if (full || ((unsigned)iz < (unsigned)D && (unsigned)iy < (unsigned)H && (unsigned)ix < (unsigned)W)) {\n v = x[nc_base + ((long long)iz * H + iy) * W + ix];\n }\n sh[s] = v;\n }\n __syncthreads();\n\n int tid = threadIdx.x;\n int gw = tid & 7; // 8 groups across W, four adjacent outputs per thread\n int tmp = tid >> 3;\n int lh = tmp & 7;\n int ld = tmp >> 3;\n\n int ow = ow_base + (gw << 2);\n int oh = oh_base + lh;\n int od = od_base + ld;\n\n if (od < Do && oh < Ho && ow < Wo) {\n int sx = gw << 3;\n int sy = lh << 1;\n int sz = ld << 1;\n float s0 = 0.0f, s1 = 0.0f, s2 = 0.0f, s3 = 0.0f;\n\n #pragma unroll\n for (int dz = 0; dz < 3; ++dz) {\n #pragma unroll\n for (int dy = 0; dy < 3; ++dy) {\n const float* p = sh + ((sz + dz) * TH + (sy + dy)) * TW + sx;\n float a0 = p[0], a1 = p[1], a2 = p[2], a3 = p[3], a4 = p[4];\n float a5 = p[5], a6 = p[6], a7 = p[7], a8 = p[8];\n s0 += a0 + a1 + a2;\n s1 += a2 + a3 + a4;\n s2 += a4 + a5 + a6;\n s3 += a6 + a7 + a8;\n }\n }\n\n long long out = (((long long)nc * Do + od) * Ho + oh) * Wo + ow;\n const float scale = 1.0f / 27.0f;\n y[out] = s0 * scale;\n if (ow + 1 < Wo) y[out + 1] = s1 * scale;\n if (ow + 2 < Wo) y[out + 2] = s2 * scale;\n if (ow + 3 < Wo) y[out + 3] = s3 * scale;\n }\n}\n\n__global__ void avgpool3d_generic_kernel(\n const float* __restrict__ x, float* __restrict__ y,\n int N, int C, int D, int H, int W,\n int Do, int Ho, int Wo, int K, int S, int P, long long total) {\n long long idx = (long long)blockIdx.x * blockDim.x + threadIdx.x;\n long long stride_grid = (long long)blockDim.x * gridDim.x;\n float inv = 1.0f / (float)(K * K * K);\n\n for (; idx < total; idx += stride_grid) {\n int ow = idx % Wo;\n long long t = idx / Wo;\n int oh = t % Ho; t /= Ho;\n int od = t % Do; t /= Do;\n int c = t % C;\n int n = t / C;\n\n int id0 = od * S - P;\n int ih0 = oh * S - P;\n int iw0 = ow * S - P;\n float sum = 0.0f;\n long long nc_base = (((long long)n * C + c) * D * H * W);\n\n for (int dz = 0; dz < K; ++dz) {\n int iz = id0 + dz;\n if ((unsigned)iz < (unsigned)D) {\n for (int dy = 0; dy < K; ++dy) {\n int iy = ih0 + dy;\n if ((unsigned)iy < (unsigned)H) {\n for (int dx = 0; dx < K; ++dx) {\n int ix = iw0 + dx;\n if ((unsigned)ix < (unsigned)W) {\n sum += x[nc_base + ((long long)iz * H + iy) * W + ix];\n }\n }\n }\n }\n }\n }\n y[idx] = sum * inv;\n }\n}\n\ntorch::Tensor avg_pool3d_forward(torch::Tensor x, int64_t kernel_size, int64_t stride, int64_t padding) {\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.dim() == 5, \"x must be 5D\");\n\n auto xc = x.contiguous();\n int N = (int)xc.size(0);\n int C = (int)xc.size(1);\n int D = (int)xc.size(2);\n int H = (int)xc.size(3);\n int W = (int)xc.size(4);\n int K = (int)kernel_size;\n int S = (int)stride;\n int P = (int)padding;\n\n int Do = (D + 2 * P - K) / S + 1;\n int Ho = (H + 2 * P - K) / S + 1;\n int Wo = (W + 2 * P - K) / S + 1;\n auto y = torch::empty({N, C, Do, Ho, Wo}, xc.options());\n\n long long total = (long long)N * C * Do * Ho * Wo;\n if (total == 0) return y;\n\n cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n if (K == 3 && S == 2 && P == 1) {\n dim3 block(256);\n dim3 grid((Wo + BW - 1) / BW,\n ((Ho + BH - 1) / BH) * ((Do + BD - 1) / BD),\n N * C);\n avgpool3d_k3s2p1_shared4_kernel<<<grid, block, 0, stream>>>(\n xc.data_ptr<float>(), y.data_ptr<float>(), D, H, W, Do, Ho, Wo, C);\n } else {\n int threads = 256;\n int blocks = (int)((total + threads - 1) / threads);\n if (blocks > 65535) blocks = 65535;\n avgpool3d_generic_kernel<<<blocks, threads, 0, stream>>>(\n xc.data_ptr<float>(), y.data_ptr<float>(), N, C, D, H, W, Do, Ho, Wo, K, S, P, total);\n }\n return y;\n}\n'''\n\n_ext = load_inline(\n name='avgpool3d_ext_final',\n cpp_sources=_cpp_src,\n cuda_sources=_cuda_src,\n functions=['avg_pool3d_forward'],\n extra_cuda_cflags=['-O3'],\n verbose=False,\n)\n\nclass ModelNew(nn.Module):\n def __init__(self, kernel_size: int, stride: int = None, padding: int = 0):\n super(ModelNew, self).__init__()\n self.kernel_size = int(kernel_size)\n self.stride = int(stride) if stride is not None else int(kernel_size)\n self.padding = int(padding)\n\n def forward(self, x: torch.Tensor) -> torch.Tensor:\n return _ext.avg_pool3d_forward(x, self.kernel_size, self.stride, self.padding)\n" }
result ← submit_kernel (ok)
oksubmit_kernel submit_kernel PASSED: (5 / 5) correctness trials all passed. Kernel runtime: 8.62 μs Runtime stats: mean=8.62μs median=?μs std=0.16μs Numerical precision: max_abs_err=2.98e-07 mean_abs_err=3.44e-08 max_rel_err=5.62e-07 Memory: 9216.0 MB peak (ref: 9216.0 MB, ratio: 1.0x) Kernel launches: 1 (ref: 5, fusion ratio: 5.0) Energy: 3743.42 mJ/run (ref: 5265.56 mJ, ratio: 1.4066x) SOL score: 0.3361
final submitted kernel (204 lines, 6,672 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 avg_pool3d_forward(torch::Tensor x, int64_t kernel_size, 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 BD 4
#define BH 8
#define BW 32
#define TD (2*BD+1)
#define TH (2*BH+1)
#define TW (2*BW+1)
#define TSIZE (TD*TH*TW)

__global__ __launch_bounds__(256, 4) void avgpool3d_k3s2p1_shared4_kernel(
        const float* __restrict__ x, float* __restrict__ y,
        int D, int H, int W, int Do, int Ho, int Wo, int C) {
    __shared__ float sh[TSIZE];

    int num_h_tiles = (Ho + BH - 1) / BH;
    int ht_dt = blockIdx.y;
    int ht = ht_dt % num_h_tiles;
    int dt = ht_dt / num_h_tiles;

    int ow_base = blockIdx.x * BW;
    int oh_base = ht * BH;
    int od_base = dt * BD;
    int nc = blockIdx.z;

    int iz0 = od_base * 2 - 1;
    int iy0 = oh_base * 2 - 1;
    int ix0 = ow_base * 2 - 1;
    long long nc_base = ((long long)nc) * D * H * W;

    bool full = (iz0 >= 0 && iy0 >= 0 && ix0 >= 0 &&
                 iz0 + TD <= D && iy0 + TH <= H && ix0 + TW <= W);

    for (int s = threadIdx.x; s < TSIZE; s += blockDim.x) {
        int lx = s % TW;
        int tmp = s / TW;
        int ly = tmp % TH;
        int lz = tmp / TH;
        int iz = iz0 + lz;
        int iy = iy0 + ly;
        int ix = ix0 + lx;
        float v = 0.0f;
        if (full || ((unsigned)iz < (unsigned)D && (unsigned)iy < (unsigned)H && (unsigned)ix < (unsigned)W)) {
            v = x[nc_base + ((long long)iz * H + iy) * W + ix];
        }
        sh[s] = v;
    }
    __syncthreads();

    int tid = threadIdx.x;
    int gw = tid & 7;          // 8 groups across W, four adjacent outputs per thread
    int tmp = tid >> 3;
    int lh = tmp & 7;
    int ld = tmp >> 3;

    int ow = ow_base + (gw << 2);
    int oh = oh_base + lh;
    int od = od_base + ld;

    if (od < Do && oh < Ho && ow < Wo) {
        int sx = gw << 3;
        int sy = lh << 1;
        int sz = ld << 1;
        float s0 = 0.0f, s1 = 0.0f, s2 = 0.0f, s3 = 0.0f;

        #pragma unroll
        for (int dz = 0; dz < 3; ++dz) {
            #pragma unroll
            for (int dy = 0; dy < 3; ++dy) {
                const float* p = sh + ((sz + dz) * TH + (sy + dy)) * TW + sx;
                float a0 = p[0], a1 = p[1], a2 = p[2], a3 = p[3], a4 = p[4];
                float a5 = p[5], a6 = p[6], a7 = p[7], a8 = p[8];
                s0 += a0 + a1 + a2;
                s1 += a2 + a3 + a4;
                s2 += a4 + a5 + a6;
                s3 += a6 + a7 + a8;
            }
        }

        long long out = (((long long)nc * Do + od) * Ho + oh) * Wo + ow;
        const float scale = 1.0f / 27.0f;
        y[out] = s0 * scale;
        if (ow + 1 < Wo) y[out + 1] = s1 * scale;
        if (ow + 2 < Wo) y[out + 2] = s2 * scale;
        if (ow + 3 < Wo) y[out + 3] = s3 * scale;
    }
}

__global__ void avgpool3d_generic_kernel(
        const float* __restrict__ x, float* __restrict__ y,
        int N, int C, int D, int H, int W,
        int Do, int Ho, int Wo, int K, int S, int P, long long total) {
    long long idx = (long long)blockIdx.x * blockDim.x + threadIdx.x;
    long long stride_grid = (long long)blockDim.x * gridDim.x;
    float inv = 1.0f / (float)(K * K * K);

    for (; idx < total; idx += stride_grid) {
        int ow = idx % Wo;
        long long t = idx / Wo;
        int oh = t % Ho; t /= Ho;
        int od = t % Do; t /= Do;
        int c = t % C;
        int n = t / C;

        int id0 = od * S - P;
        int ih0 = oh * S - P;
        int iw0 = ow * S - P;
        float sum = 0.0f;
        long long nc_base = (((long long)n * C + c) * D * H * W);

        for (int dz = 0; dz < K; ++dz) {
            int iz = id0 + dz;
            if ((unsigned)iz < (unsigned)D) {
                for (int dy = 0; dy < K; ++dy) {
                    int iy = ih0 + dy;
                    if ((unsigned)iy < (unsigned)H) {
                        for (int dx = 0; dx < K; ++dx) {
                            int ix = iw0 + dx;
                            if ((unsigned)ix < (unsigned)W) {
                                sum += x[nc_base + ((long long)iz * H + iy) * W + ix];
                            }
                        }
                    }
                }
            }
        }
        y[idx] = sum * inv;
    }
}

torch::Tensor avg_pool3d_forward(torch::Tensor x, int64_t kernel_size, int64_t stride, int64_t padding) {
    TORCH_CHECK(x.is_cuda(), "x must be CUDA");
    TORCH_CHECK(x.scalar_type() == at::kFloat, "only float32 supported");
    TORCH_CHECK(x.dim() == 5, "x must be 5D");

    auto xc = x.contiguous();
    int N = (int)xc.size(0);
    int C = (int)xc.size(1);
    int D = (int)xc.size(2);
    int H = (int)xc.size(3);
    int W = (int)xc.size(4);
    int K = (int)kernel_size;
    int S = (int)stride;
    int P = (int)padding;

    int Do = (D + 2 * P - K) / S + 1;
    int Ho = (H + 2 * P - K) / S + 1;
    int Wo = (W + 2 * P - K) / S + 1;
    auto y = torch::empty({N, C, Do, Ho, Wo}, xc.options());

    long long total = (long long)N * C * Do * Ho * Wo;
    if (total == 0) return y;

    cudaStream_t stream = at::cuda::getCurrentCUDAStream();
    if (K == 3 && S == 2 && P == 1) {
        dim3 block(256);
        dim3 grid((Wo + BW - 1) / BW,
                  ((Ho + BH - 1) / BH) * ((Do + BD - 1) / BD),
                  N * C);
        avgpool3d_k3s2p1_shared4_kernel<<<grid, block, 0, stream>>>(
            xc.data_ptr<float>(), y.data_ptr<float>(), D, H, W, Do, Ho, Wo, C);
    } else {
        int threads = 256;
        int blocks = (int)((total + threads - 1) / threads);
        if (blocks > 65535) blocks = 65535;
        avgpool3d_generic_kernel<<<blocks, threads, 0, stream>>>(
            xc.data_ptr<float>(), y.data_ptr<float>(), N, C, D, H, W, Do, Ho, Wo, K, S, P, total);
    }
    return y;
}
'''

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

class ModelNew(nn.Module):
    def __init__(self, kernel_size: int, stride: int = None, padding: int = 0):
        super(ModelNew, self).__init__()
        self.kernel_size = int(kernel_size)
        self.stride = int(stride) if stride is not None else int(kernel_size)
        self.padding = int(padding)

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        return _ext.avg_pool3d_forward(x, self.kernel_size, self.stride, self.padding)