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 conv2d_forward_cuda(torch::Tensor x, torch::Tensor weight, torch::Tensor bias,\n bool has_bias, int stride_h, int stride_w,\n int pad_h, int pad_w, int dil_h, int dil_w, int groups);\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 TH 4\n#define TW 32\n#define KH 5\n#define KW 9\n#define IC 32\n#define OCT 16\n#define INH (TH + KH - 1)\n#define INW (TW + KW - 1)\n\n__global__ __launch_bounds__(128, 2)\nvoid conv_32_64_5x9_kernel(const float* __restrict__ x,\n const float* __restrict__ w,\n const float* __restrict__ b,\n float* __restrict__ y,\n int N, int H, int W, int OH, int OW,\n int OC, bool has_bias) {\n const int tid = threadIdx.x;\n const int lx = tid & 31; // 0..31\n const int ly = tid >> 5; // 0..3\n const int ow0 = blockIdx.x * TW;\n const int oh0 = blockIdx.y * TH;\n const int oc_groups = OC / OCT;\n const int n = blockIdx.z / oc_groups;\n const int oc0 = (blockIdx.z - n * oc_groups) * OCT;\n\n __shared__ float xs[IC * INH * INW];\n __shared__ float ws[OCT * KH * KW];\n\n // Load the input patch for this (n, spatial tile). The final W tile reads\n // a little past the image; those entries are zero and are only used by\n // threads whose outputs are out of bounds.\n const int total_x = IC * INH * INW;\n for (int idx = tid; idx < total_x; idx += 128) {\n int t = idx;\n const int ix = t % INW; t /= INW;\n const int iy = t % INH; t /= INH;\n const int c = t;\n const int ih = oh0 + iy;\n const int iw = ow0 + ix;\n float v = 0.0f;\n if (ih < H && iw < W) {\n v = x[((n * IC + c) * H + ih) * W + iw];\n }\n xs[idx] = v;\n }\n __syncthreads();\n\n float acc[OCT];\n#pragma unroll\n for (int i = 0; i < OCT; ++i) acc[i] = 0.0f;\n\n const bool valid = (oh0 + ly < OH) && (ow0 + lx < OW);\n const int base_xy = ly * INW + lx;\n\n#pragma unroll\n for (int c = 0; c < IC; ++c) {\n // Load this input-channel's 16x5x9 weights once per block into shared\n // memory. This avoids the otherwise very expensive warp-uniform global\n // reloads of the same coefficients by all 128 threads.\n for (int idx = tid; idx < OCT * KH * KW; idx += 128) {\n const int oo = idx / (KH * KW);\n const int k = idx - oo * (KH * KW);\n ws[idx] = w[((oc0 + oo) * IC + c) * (KH * KW) + k];\n }\n __syncthreads();\n\n if (valid) {\n const float* __restrict__ xp = xs + c * (INH * INW) + base_xy;\n#pragma unroll\n for (int kh = 0; kh < KH; ++kh) {\n#pragma unroll\n for (int kw = 0; kw < KW; ++kw) {\n const int k = kh * KW + kw;\n const float xv = xp[kh * INW + kw];\n#pragma unroll\n for (int oo = 0; oo < OCT; ++oo) {\n acc[oo] = fmaf(xv, ws[oo * (KH * KW) + k], acc[oo]);\n }\n }\n }\n }\n __syncthreads();\n }\n\n if (valid) {\n const int oh = oh0 + ly;\n const int ow = ow0 + lx;\n#pragma unroll\n for (int oo = 0; oo < OCT; ++oo) {\n float v = acc[oo];\n if (has_bias) v += b[oc0 + oo];\n y[((n * OC + oc0 + oo) * OH + oh) * OW + ow] = v;\n }\n }\n}\n\ntorch::Tensor conv2d_forward_cuda(torch::Tensor x, torch::Tensor weight, torch::Tensor bias,\n bool has_bias, int stride_h, int stride_w,\n int pad_h, int pad_w, int dil_h, int dil_w, int groups) {\n TORCH_CHECK(x.is_cuda() && weight.is_cuda(), \"inputs must be CUDA tensors\");\n TORCH_CHECK(x.scalar_type() == torch::kFloat32 && weight.scalar_type() == torch::kFloat32, \"float32 only\");\n TORCH_CHECK(x.dim() == 4 && weight.dim() == 4, \"expected NCHW input and OIHW weight\");\n TORCH_CHECK(stride_h == 1 && stride_w == 1 && pad_h == 0 && pad_w == 0 && dil_h == 1 && dil_w == 1 && groups == 1,\n \"this optimized kernel supports only stride=1, padding=0, dilation=1, groups=1\");\n TORCH_CHECK(x.size(1) == 32 && weight.size(1) == 32 && weight.size(2) == 5 && weight.size(3) == 9,\n \"this optimized kernel supports C=32 and kernel 5x9\");\n TORCH_CHECK(weight.size(0) % 16 == 0, \"out_channels must be divisible by 16\");\n\n x = x.contiguous();\n weight = weight.contiguous();\n if (has_bias) bias = bias.contiguous();\n\n const int N = (int)x.size(0);\n const int H = (int)x.size(2);\n const int W = (int)x.size(3);\n const int OC = (int)weight.size(0);\n const int OH = H - 5 + 1;\n const int OW = W - 9 + 1;\n auto y = torch::empty({N, OC, OH, OW}, x.options());\n\n dim3 block(128);\n dim3 grid((OW + TW - 1) / TW, (OH + TH - 1) / TH, N * (OC / OCT));\n conv_32_64_5x9_kernel<<<grid, block, 0, at::cuda::getCurrentCUDAStream()>>>(\n x.data_ptr<float>(), weight.data_ptr<float>(), has_bias ? bias.data_ptr<float>() : nullptr,\n y.data_ptr<float>(), N, H, W, OH, OW, OC, has_bias);\n C10_CUDA_KERNEL_LAUNCH_CHECK();\n return y;\n}\n\"\"\"\n\n_ext = load_inline(\n name=\"conv2d_5x9_32c_ext\",\n cpp_sources=_cpp_src,\n cuda_sources=_cuda_src,\n functions=[\"conv2d_forward_cuda\"],\n extra_cuda_cflags=[\"-O3\", \"--use_fast_math\"],\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: int = 1, padding: int = 0, dilation: int = 1, groups: int = 1, bias: bool = False):\n super(ModelNew, self).__init__()\n self.conv2d = nn.Conv2d(in_channels, out_channels, kernel_size, stride=stride, padding=padding, dilation=dilation, groups=groups, bias=bias)\n if isinstance(stride, tuple):\n self._stride_h, self._stride_w = int(stride[0]), int(stride[1])\n else:\n self._stride_h = self._stride_w = int(stride)\n if isinstance(padding, tuple):\n self._pad_h, self._pad_w = int(padding[0]), int(padding[1])\n else:\n self._pad_h = self._pad_w = int(padding)\n if isinstance(dilation, tuple):\n self._dil_h, self._dil_w = int(dilation[0]), int(dilation[1])\n else:\n self._dil_h = self._dil_w = int(dilation)\n self._groups = int(groups)\n\n def forward(self, x: torch.Tensor) -> torch.Tensor:\n bias_t = self.conv2d.bias if self.conv2d.bias is not None else self.conv2d.weight\n return _ext.conv2d_forward_cuda(x, self.conv2d.weight, bias_t, self.conv2d.bias is not None,\n self._stride_h, self._stride_w, self._pad_h, self._pad_w,\n self._dil_h, self._dil_w, self._groups)\n"
}