call → submit_kernel
callsubmit_kernel
{
"kernel_code": "import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.utils.cpp_extension import load_inline\n\n\n_cpp_src = r'''\n#include <torch/extension.h>\n#include <ATen/cuda/CUDAContext.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\nvoid relu_maxpool_launcher(const at::Tensor& input, at::Tensor& output);\nvoid avg_fc_launcher(const at::Tensor& input, const at::Tensor& weight, const at::Tensor& bias, at::Tensor& output);\n\ntorch::Tensor relu_maxpool_forward(torch::Tensor input) {\n CHECK_CUDA(input); CHECK_CONTIGUOUS(input); CHECK_FLOAT(input);\n TORCH_CHECK(input.dim() == 4, \"input must be NCHW\");\n const auto N = input.size(0);\n const auto C = input.size(1);\n const auto H = input.size(2);\n const auto W = input.size(3);\n TORCH_CHECK((H >= 2) && (W >= 2), \"spatial dims too small\");\n auto output = torch::empty({N, C, H / 2, W / 2}, input.options());\n relu_maxpool_launcher(input, output);\n return output;\n}\n\ntorch::Tensor avg_fc_forward(torch::Tensor input, torch::Tensor weight, torch::Tensor bias) {\n CHECK_CUDA(input); CHECK_CONTIGUOUS(input); CHECK_FLOAT(input);\n CHECK_CUDA(weight); CHECK_CONTIGUOUS(weight); CHECK_FLOAT(weight);\n CHECK_CUDA(bias); CHECK_CONTIGUOUS(bias); CHECK_FLOAT(bias);\n TORCH_CHECK(input.dim() == 4, \"input must be NCHW\");\n TORCH_CHECK(weight.dim() == 2, \"weight must be 2D\");\n TORCH_CHECK(bias.dim() == 1, \"bias must be 1D\");\n TORCH_CHECK(input.size(1) == weight.size(1), \"channel mismatch\");\n TORCH_CHECK(weight.size(0) == bias.size(0), \"class mismatch\");\n auto output = torch::empty({input.size(0), weight.size(0)}, input.options());\n avg_fc_launcher(input, weight, bias, output);\n return output;\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#include <cfloat>\n\n__global__ void relu_maxpool_kernel(const float* __restrict__ x,\n float* __restrict__ y,\n long total, int C, int H, int W, int Ho, int Wo) {\n long idx = (long)blockIdx.x * blockDim.x + threadIdx.x;\n if (idx >= total) return;\n int ow = idx % Wo;\n long t = idx / Wo;\n int oh = t % Ho;\n t /= Ho;\n int c = t % C;\n int n = t / C;\n\n long base = ((long)n * C + c) * (long)H * W + (long)(oh * 2) * W + (ow * 2);\n float v0 = x[base];\n float v1 = x[base + 1];\n float v2 = x[base + W];\n float v3 = x[base + W + 1];\n float m = fmaxf(fmaxf(v0, v1), fmaxf(v2, v3));\n y[idx] = fmaxf(m, 0.0f);\n}\n\nvoid relu_maxpool_launcher(const at::Tensor& input, at::Tensor& output) {\n const int C = (int)input.size(1);\n const int H = (int)input.size(2);\n const int W = (int)input.size(3);\n const int Ho = (int)output.size(2);\n const int Wo = (int)output.size(3);\n const long total = output.numel();\n const int threads = 256;\n const int blocks = (int)((total + threads - 1) / threads);\n relu_maxpool_kernel<<<blocks, threads, 0, at::cuda::getCurrentCUDAStream()>>>(\n input.data_ptr<float>(), output.data_ptr<float>(), total, C, H, W, Ho, Wo);\n}\n\n__global__ void avg_fc_kernel(const float* __restrict__ x,\n const float* __restrict__ w,\n const float* __restrict__ b,\n float* __restrict__ y,\n int N, int C, int H, int W, int K) {\n int nk = blockIdx.x;\n int k = nk % K;\n int n = nk / K;\n int tid = threadIdx.x;\n int HW = H * W;\n int CHW = C * HW;\n float sum = 0.0f;\n const float* xp = x + (long)n * CHW;\n const float* wp = w + (long)k * C;\n for (int idx = tid; idx < CHW; idx += blockDim.x) {\n int c = idx / HW;\n sum += xp[idx] * wp[c];\n }\n __shared__ float smem[256];\n smem[tid] = sum;\n __syncthreads();\n for (int s = blockDim.x >> 1; s > 0; s >>= 1) {\n if (tid < s) smem[tid] += smem[tid + s];\n __syncthreads();\n }\n if (tid == 0) {\n y[(long)n * K + k] = b[k] + smem[0] / (float)HW;\n }\n}\n\nvoid avg_fc_launcher(const at::Tensor& input, const at::Tensor& weight, const at::Tensor& bias, at::Tensor& output) {\n const int N = (int)input.size(0);\n const int C = (int)input.size(1);\n const int H = (int)input.size(2);\n const int W = (int)input.size(3);\n const int K = (int)weight.size(0);\n const int threads = 256;\n const int blocks = N * K;\n avg_fc_kernel<<<blocks, threads, 0, at::cuda::getCurrentCUDAStream()>>>(\n input.data_ptr<float>(), weight.data_ptr<float>(), bias.data_ptr<float>(),\n output.data_ptr<float>(), N, C, H, W, K);\n}\n'''\n\n_ext = load_inline(\n name=\"regnet_fused_ext_v1\",\n cpp_sources=_cpp_src,\n cuda_sources=_cuda_src,\n functions=[\"relu_maxpool_forward\", \"avg_fc_forward\"],\n extra_cflags=[\"-O3\"],\n extra_cuda_cflags=[\"-O3\"],\n verbose=False,\n)\n\n\nclass ModelNew(nn.Module):\n def __init__(self, input_channels, stages, block_widths, output_classes):\n super(ModelNew, self).__init__()\n self.stages = stages\n self.block_widths = block_widths\n layers = []\n current_channels = input_channels\n for i in range(stages):\n layers.append(self._make_stage(current_channels, block_widths[i]))\n current_channels = block_widths[i]\n self.feature_extractor = nn.Sequential(*layers)\n self.fc = nn.Linear(block_widths[-1], output_classes)\n self._fold_cache_key = None\n self._fold_cache = None\n\n def _make_stage(self, in_channels, out_channels):\n return nn.Sequential(\n nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1),\n nn.BatchNorm2d(out_channels),\n nn.ReLU(),\n nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1),\n nn.BatchNorm2d(out_channels),\n nn.ReLU(),\n nn.MaxPool2d(kernel_size=2, stride=2)\n )\n\n def _cache_key(self):\n key = []\n for i in range(self.stages):\n st = self.feature_extractor[i]\n for conv, bn in ((st[0], st[1]), (st[3], st[4])):\n ts = (conv.weight, conv.bias, bn.weight, bn.bias, bn.running_mean, bn.running_var)\n for t in ts:\n key.append((t.data_ptr(), t._version, tuple(t.shape), str(t.device)))\n return tuple(key)\n\n def _get_folded(self):\n key = self._cache_key()\n if self._fold_cache_key == key and self._fold_cache is not None:\n return self._fold_cache\n folded = []\n with torch.no_grad():\n for i in range(self.stages):\n st = self.feature_extractor[i]\n for conv, bn in ((st[0], st[1]), (st[3], st[4])):\n scale = bn.weight * torch.rsqrt(bn.running_var + bn.eps)\n w = (conv.weight * scale.reshape(-1, 1, 1, 1)).contiguous()\n if conv.bias is None:\n cb = torch.zeros_like(bn.running_mean)\n else:\n cb = conv.bias\n b = ((cb - bn.running_mean) * scale + bn.bias).contiguous()\n folded.append((w, b))\n self._fold_cache_key = key\n self._fold_cache = folded\n return folded\n\n def forward(self, x):\n # Fast inference path: fold BatchNorm into convolutions and fuse ReLU+2x2 maxpool.\n if not self.training:\n folded = self._get_folded()\n p = 0\n for i in range(self.stages):\n w1, b1 = folded[p]; w2, b2 = folded[p + 1]; p += 2\n x = F.conv2d(x, w1, b1, padding=1)\n x = torch.relu_(x)\n x = F.conv2d(x, w2, b2, padding=1)\n x = _ext.relu_maxpool_forward(x.contiguous())\n return _ext.avg_fc_forward(x.contiguous(), self.fc.weight.contiguous(), self.fc.bias.contiguous())\n\n # Training-mode-correct path: use PyTorch conv/BN for exact batch statistics, but\n # still avoid extra ReLU allocation and fuse ReLU with maxpool and final GAP+FC.\n for i in range(self.stages):\n st = self.feature_extractor[i]\n conv1, bn1, conv2, bn2 = st[0], st[1], st[3], st[4]\n x = F.conv2d(x, conv1.weight, conv1.bias, padding=1)\n x = F.batch_norm(x, bn1.running_mean, bn1.running_var, bn1.weight, bn1.bias,\n True, bn1.momentum, bn1.eps)\n x = torch.relu_(x)\n x = F.conv2d(x, conv2.weight, conv2.bias, padding=1)\n x = F.batch_norm(x, bn2.running_mean, bn2.running_var, bn2.weight, bn2.bias,\n True, bn2.momentum, bn2.eps)\n x = _ext.relu_maxpool_forward(x.contiguous())\n return _ext.avg_fc_forward(x.contiguous(), self.fc.weight.contiguous(), self.fc.bias.contiguous())\n"
}