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\ntorch::Tensor depthwise_shuffle_cuda(torch::Tensor input, torch::Tensor weight, torch::Tensor bias, int64_t groups);\nvoid relu_inplace_cuda(torch::Tensor input);\ntorch::Tensor relu_add_cuda(torch::Tensor main, torch::Tensor shortcut);\n'''\n\n_cuda_src = r'''\n#include <torch/extension.h>\n#include <ATen/cuda/CUDAContext.h>\n#include <c10/cuda/CUDAGuard.h>\n\n#define THREADS 256\n\n__global__ void relu_inplace_kernel(float* __restrict__ x, long long n) {\n long long i = (long long)blockIdx.x * blockDim.x + threadIdx.x;\n if (i < n) {\n float v = x[i];\n x[i] = v > 0.0f ? v : 0.0f;\n }\n}\n\n__global__ void relu_add_kernel(const float* __restrict__ main,\n float* __restrict__ shortcut,\n long long n) {\n long long i = (long long)blockIdx.x * blockDim.x + threadIdx.x;\n if (i < n) {\n float v = main[i];\n shortcut[i] += (v > 0.0f ? v : 0.0f);\n }\n}\n\n__global__ void depthwise_shuffle_kernel(const float* __restrict__ inp,\n const float* __restrict__ w,\n const float* __restrict__ b,\n float* __restrict__ out,\n int N, int C, int H, int W, int G,\n long long total) {\n long long idx = (long long)blockIdx.x * blockDim.x + threadIdx.x;\n if (idx >= total) return;\n\n int x = (int)(idx % W);\n long long t = idx / W;\n int y = (int)(t % H);\n t /= H;\n int sc = (int)(t % C); // channel after ChannelShuffle\n int n = (int)(t / C);\n\n int cpg = C / G;\n int g = sc % G;\n int k = sc / G;\n int c = g * cpg + k; // original depthwise channel before shuffle\n\n float acc = b[c];\n long long base_n = ((long long)n * C + c) * H * W;\n long long wbase = (long long)c * 9;\n\n // 3x3 depthwise convolution, padding=1, stride=1, dilation=1 (cross-correlation)\n if (y > 0) {\n long long row = base_n + (long long)(y - 1) * W;\n if (x > 0) acc += inp[row + x - 1] * w[wbase + 0];\n acc += inp[row + x ] * w[wbase + 1];\n if (x + 1 < W) acc += inp[row + x + 1] * w[wbase + 2];\n }\n {\n long long row = base_n + (long long)y * W;\n if (x > 0) acc += inp[row + x - 1] * w[wbase + 3];\n acc += inp[row + x ] * w[wbase + 4];\n if (x + 1 < W) acc += inp[row + x + 1] * w[wbase + 5];\n }\n if (y + 1 < H) {\n long long row = base_n + (long long)(y + 1) * W;\n if (x > 0) acc += inp[row + x - 1] * w[wbase + 6];\n acc += inp[row + x ] * w[wbase + 7];\n if (x + 1 < W) acc += inp[row + x + 1] * w[wbase + 8];\n }\n\n out[idx] = acc;\n}\n\nvoid relu_inplace_cuda(torch::Tensor input) {\n const at::cuda::OptionalCUDAGuard device_guard(device_of(input));\n long long n = input.numel();\n if (n == 0) return;\n int blocks = (int)((n + THREADS - 1) / THREADS);\n relu_inplace_kernel<<<blocks, THREADS, 0, at::cuda::getCurrentCUDAStream()>>>(\n input.data_ptr<float>(), n);\n}\n\ntorch::Tensor relu_add_cuda(torch::Tensor main, torch::Tensor shortcut) {\n const at::cuda::OptionalCUDAGuard device_guard(device_of(main));\n long long n = main.numel();\n if (n) {\n int blocks = (int)((n + THREADS - 1) / THREADS);\n relu_add_kernel<<<blocks, THREADS, 0, at::cuda::getCurrentCUDAStream()>>>(\n main.data_ptr<float>(), shortcut.data_ptr<float>(), n);\n }\n return shortcut;\n}\n\ntorch::Tensor depthwise_shuffle_cuda(torch::Tensor input, torch::Tensor weight, torch::Tensor bias, int64_t groups) {\n const at::cuda::OptionalCUDAGuard device_guard(device_of(input));\n auto out = torch::empty_like(input);\n int N = (int)input.size(0);\n int C = (int)input.size(1);\n int H = (int)input.size(2);\n int W = (int)input.size(3);\n long long total = input.numel();\n if (total) {\n int blocks = (int)((total + THREADS - 1) / THREADS);\n depthwise_shuffle_kernel<<<blocks, THREADS, 0, at::cuda::getCurrentCUDAStream()>>>(\n input.data_ptr<float>(), weight.data_ptr<float>(), bias.data_ptr<float>(), out.data_ptr<float>(),\n N, C, H, W, (int)groups, total);\n }\n return out;\n}\n'''\n\n_ext = load_inline(\n name='shufflenet_unit_fused_ext_v1',\n cpp_sources=_cpp_src,\n cuda_sources=_cuda_src,\n functions=['depthwise_shuffle_cuda', 'relu_inplace_cuda', 'relu_add_cuda'],\n extra_cflags=['-O3'],\n extra_cuda_cflags=['-O3', '--use_fast_math'],\n verbose=False,\n)\n\n\nclass ModelNew(nn.Module):\n def __init__(self, in_channels, out_channels, groups=3):\n super(ModelNew, self).__init__()\n assert out_channels % 4 == 0\n mid_channels = out_channels // 4\n self.groups = groups\n self.in_channels = in_channels\n self.out_channels = out_channels\n self.mid_channels = mid_channels\n\n self.conv1 = nn.Conv2d(in_channels, mid_channels, kernel_size=1, stride=1, padding=0, groups=groups, bias=False)\n self.bn1 = nn.BatchNorm2d(mid_channels)\n self.conv2 = nn.Conv2d(mid_channels, mid_channels, kernel_size=3, stride=1, padding=1, groups=mid_channels, bias=False)\n self.bn2 = nn.BatchNorm2d(mid_channels)\n self.conv3 = nn.Conv2d(mid_channels, out_channels, kernel_size=1, stride=1, padding=0, groups=groups, bias=False)\n self.bn3 = nn.BatchNorm2d(out_channels)\n\n if in_channels == out_channels:\n self.shortcut = nn.Sequential()\n else:\n self.shortcut = nn.Sequential(\n nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=1, padding=0, bias=False),\n nn.BatchNorm2d(out_channels)\n )\n self._fused_valid = False\n\n def _clear_fused(self):\n self._fused_valid = False\n for n in ('_w1','_b1','_w2','_b2','_w3','_b3','_ws','_bs'):\n if hasattr(self, n):\n setattr(self, n, None)\n\n def _apply(self, fn):\n ret = super()._apply(fn)\n self._clear_fused()\n return ret\n\n def load_state_dict(self, *args, **kwargs):\n ret = super().load_state_dict(*args, **kwargs)\n self._clear_fused()\n return ret\n\n @staticmethod\n def _fold(conv, bn):\n scale = bn.weight * torch.rsqrt(bn.running_var + bn.eps)\n w = conv.weight * scale.reshape(-1, 1, 1, 1)\n b = bn.bias - bn.running_mean * scale\n return w.contiguous(), b.contiguous()\n\n def _ensure_fused(self, x):\n if self._fused_valid and getattr(self, '_fused_device', None) == x.device:\n return\n with torch.no_grad():\n self._w1, self._b1 = self._fold(self.conv1, self.bn1)\n self._w2, self._b2 = self._fold(self.conv2, self.bn2)\n self._w3, self._b3 = self._fold(self.conv3, self.bn3)\n if len(self.shortcut) == 0:\n self._ws = None\n self._bs = None\n else:\n self._ws, self._bs = self._fold(self.shortcut[0], self.shortcut[1])\n self._fused_device = x.device\n self._fused_valid = True\n\n def _shuffle_torch(self, x):\n n, c, h, w = x.shape\n return x.view(n, self.groups, c // self.groups, h, w).transpose(1, 2).contiguous().view(n, c, h, w)\n\n def forward(self, x):\n # Exact training-mode behavior (BatchNorm uses batch statistics). The optimized\n # inference path below is used when the module is in eval mode, as in benchmarking.\n if self.training:\n out = F.relu(self.bn1(self.conv1(x)))\n out = self.bn2(self.conv2(out))\n out = self._shuffle_torch(out)\n out = F.relu(self.bn3(self.conv3(out)))\n out = out + self.shortcut(x)\n return out\n\n self._ensure_fused(x)\n\n out = F.conv2d(x, self._w1, self._b1, None, 0, 1, self.groups)\n _ext.relu_inplace_cuda(out)\n\n # Depthwise 3x3 with folded BN2, immediately written in ChannelShuffle order.\n out = _ext.depthwise_shuffle_cuda(out, self._w2, self._b2, self.groups)\n\n out = F.conv2d(out, self._w3, self._b3, None, 0, 1, self.groups)\n\n if self._ws is None:\n shortcut = x.clone()\n else:\n shortcut = F.conv2d(x, self._ws, self._bs, None, 0, 1, 1)\n\n return _ext.relu_add_cuda(out, shortcut)\n"
}