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_cpp_src = r\"\"\"\n#include <torch/extension.h>\n\ntorch::Tensor depthwise_cuda(torch::Tensor x, torch::Tensor w, torch::Tensor bias, bool has_bias,\n int64_t stride, int64_t padding, int64_t dilation);\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 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\n// Optimized path for the benchmark case: k=3, stride=1, padding=1, dilation=1.\n// One CTA computes a 32x16 spatial tile for one (N,C) plane. The input tile\n// including halo is staged in shared memory, reducing the 3x3 depthwise pass to\n// about one global load per output element.\n__global__ __launch_bounds__(512, 2)\nvoid dw3x3s1p1_kernel(const float* __restrict__ x,\n const float* __restrict__ w,\n const float* __restrict__ b,\n float* __restrict__ y,\n int NC, int H, int W) {\n constexpr int TW = 32;\n constexpr int TH = 16;\n constexpr int SW = TW + 2;\n constexpr int SH = TH + 2;\n __shared__ float sh[SH * SW];\n\n const int tx = threadIdx.x;\n const int ty = threadIdx.y;\n const int tid = ty * blockDim.x + tx;\n const int z = blockIdx.z; // n*C + c\n const int c = z % NC; // actually C is passed as NC? see below no\n}\n\n__global__ __launch_bounds__(512, 2)\nvoid dw3x3s1p1_kernel2(const float* __restrict__ x,\n const float* __restrict__ w,\n const float* __restrict__ b,\n float* __restrict__ y,\n int C, int H, int W) {\n constexpr int TW = 32;\n constexpr int TH = 16;\n constexpr int SW = TW + 2;\n constexpr int SH = TH + 2;\n __shared__ float sh[SH * SW];\n\n const int tx = threadIdx.x;\n const int ty = threadIdx.y;\n const int tid = ty * 32 + tx;\n const int z = blockIdx.z; // n*C + c\n const int c = z % C;\n const int ox0 = blockIdx.x * TW;\n const int oy0 = blockIdx.y * TH;\n\n // Cooperative load of the 34x18 tile including zero-padding halo.\n #pragma unroll\n for (int i = 0; i < SH * SW; i += 512) {\n int j = i + tid;\n if (j < SH * SW) {\n int sy = j / SW;\n int sx = j - sy * SW;\n int iy = oy0 + sy - 1;\n int ix = ox0 + sx - 1;\n float v = 0.0f;\n if ((unsigned)iy < (unsigned)H && (unsigned)ix < (unsigned)W) {\n v = x[((long long)z * H + iy) * W + ix];\n }\n sh[j] = v;\n }\n }\n __syncthreads();\n\n const int ox = ox0 + tx;\n const int oy = oy0 + ty;\n if (ox < W && oy < H) {\n const float* __restrict__ wc = w + c * 9;\n float acc = 0.0f;\n acc = fmaf(wc[0], sh[(ty + 0) * SW + tx + 0], acc);\n acc = fmaf(wc[1], sh[(ty + 0) * SW + tx + 1], acc);\n acc = fmaf(wc[2], sh[(ty + 0) * SW + tx + 2], acc);\n acc = fmaf(wc[3], sh[(ty + 1) * SW + tx + 0], acc);\n acc = fmaf(wc[4], sh[(ty + 1) * SW + tx + 1], acc);\n acc = fmaf(wc[5], sh[(ty + 1) * SW + tx + 2], acc);\n acc = fmaf(wc[6], sh[(ty + 2) * SW + tx + 0], acc);\n acc = fmaf(wc[7], sh[(ty + 2) * SW + tx + 1], acc);\n acc = fmaf(wc[8], sh[(ty + 2) * SW + tx + 2], acc);\n if (b != nullptr) acc += b[c];\n y[((long long)z * H + oy) * W + ox] = acc;\n }\n}\n\n__global__ void dw_generic_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 Ho, int Wo, int stride, int pad, int dil) {\n long long total = (long long)N * C * Ho * Wo;\n long long idx = (long long)blockIdx.x * blockDim.x + threadIdx.x;\n long long step = (long long)blockDim.x * gridDim.x;\n for (; idx < total; idx += step) {\n int ow = idx % Wo;\n long long t = idx / Wo;\n int oh = t % Ho;\n t /= Ho;\n int c = t % C;\n int n = t / C;\n float acc = 0.0f;\n int base_y = oh * stride - pad;\n int base_x = ow * stride - pad;\n const float* __restrict__ wc = w + (long long)c * K * K;\n for (int ky = 0; ky < K; ++ky) {\n int iy = base_y + ky * dil;\n if ((unsigned)iy >= (unsigned)H) continue;\n for (int kx = 0; kx < K; ++kx) {\n int ix = base_x + kx * dil;\n if ((unsigned)ix < (unsigned)W) {\n float xv = x[((long long)n * C + c) * H * W + (long long)iy * W + ix];\n acc = fmaf(wc[ky * K + kx], xv, acc);\n }\n }\n }\n if (b != nullptr) acc += b[c];\n y[idx] = acc;\n }\n}\n\ntorch::Tensor depthwise_cuda(torch::Tensor x, torch::Tensor w, torch::Tensor bias, bool has_bias,\n int64_t stride, int64_t padding, int64_t dilation) {\n CHECK_CUDA(x); CHECK_CUDA(w);\n CHECK_CONTIGUOUS(x); CHECK_CONTIGUOUS(w);\n const at::cuda::OptionalCUDAGuard device_guard(device_of(x));\n\n const int N = (int)x.size(0);\n const int C = (int)x.size(1);\n const int H = (int)x.size(2);\n const int W = (int)x.size(3);\n const int K = (int)w.size(2);\n const int Ho = (H + 2 * (int)padding - (int)dilation * (K - 1) - 1) / (int)stride + 1;\n const int Wo = (W + 2 * (int)padding - (int)dilation * (K - 1) - 1) / (int)stride + 1;\n\n auto y = torch::empty({N, C, Ho, Wo}, x.options());\n const float* bp = has_bias ? bias.data_ptr<float>() : nullptr;\n cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n\n if (K == 3 && stride == 1 && padding == 1 && dilation == 1 && Ho == H && Wo == W) {\n dim3 block(32, 16, 1);\n dim3 grid((W + 31) / 32, (H + 15) / 16, N * C);\n dw3x3s1p1_kernel2<<<grid, block, 0, stream>>>(x.data_ptr<float>(), w.data_ptr<float>(), bp,\n y.data_ptr<float>(), C, H, W);\n } else {\n int threads = 256;\n int blocks = (int)std::min<long long>(65535LL, ((long long)N * C * Ho * Wo + threads - 1) / threads);\n dw_generic_kernel<<<blocks, threads, 0, stream>>>(x.data_ptr<float>(), w.data_ptr<float>(), bp,\n y.data_ptr<float>(), N, C, H, W, K, Ho, Wo,\n (int)stride, (int)padding, (int)dilation);\n }\n return y;\n}\n\"\"\"\n\n_ext = load_inline(\n name=\"dw_sep_depthwise_ext_v1\",\n cpp_sources=_cpp_src,\n cuda_sources=_cuda_src,\n functions=[\"depthwise_cuda\"],\n extra_cflags=[\"-O3\"],\n extra_cuda_cflags=[\"-O3\", \"--use_fast_math\"],\n verbose=False,\n)\n\nclass ModelNew(nn.Module):\n def __init__(self, in_channels: int, out_channels: int, kernel_size: int,\n stride: int = 1, padding: int = 0, dilation: int = 1, bias: bool = False):\n super(ModelNew, self).__init__()\n self.depthwise = nn.Conv2d(in_channels, in_channels, kernel_size, stride=stride,\n padding=padding, dilation=dilation, groups=in_channels, bias=bias)\n self.pointwise = nn.Conv2d(in_channels, out_channels, kernel_size=1, bias=bias)\n self.stride = int(stride)\n self.padding = int(padding)\n self.dilation = int(dilation)\n self.register_buffer(\"_empty_bias\", torch.empty(0), persistent=False)\n\n def forward(self, x: torch.Tensor) -> torch.Tensor:\n # Custom CUDA depthwise pass, followed by the highly optimized library 1x1 convolution.\n db = self.depthwise.bias\n if db is None:\n dw = _ext.depthwise_cuda(x, self.depthwise.weight, self._empty_bias, False,\n self.stride, self.padding, self.dilation)\n else:\n dw = _ext.depthwise_cuda(x, self.depthwise.weight, db, True,\n self.stride, self.padding, self.dilation)\n return F.conv2d(dw, self.pointwise.weight, self.pointwise.bias, stride=1, padding=0, dilation=1, groups=1)\n"
}