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#include <c10/cuda/CUDAGuard.h>\n\nvoid bn_scale_cuda(torch::Tensor y, torch::Tensor gamma, torch::Tensor beta,\n torch::Tensor running_mean, torch::Tensor running_var,\n torch::Tensor out, torch::Tensor a, torch::Tensor b,\n torch::Tensor psum, torch::Tensor psq,\n bool training, double momentum, double eps, double scaling);\n\ntorch::Tensor bn_scale_forward(torch::Tensor y, torch::Tensor gamma, torch::Tensor beta,\n torch::Tensor running_mean, torch::Tensor running_var,\n bool training, double momentum, double eps, double scaling) {\n TORCH_CHECK(y.is_cuda(), \"y must be CUDA\");\n TORCH_CHECK(y.scalar_type() == torch::kFloat32, \"only float32 supported\");\n c10::cuda::CUDAGuard device_guard(y.device());\n y = y.contiguous();\n int64_t C = y.size(1);\n auto out = torch::empty_like(y);\n auto a = torch::empty({C}, y.options());\n auto b = torch::empty({C}, y.options());\n auto dopts = y.options().dtype(torch::kFloat64);\n torch::Tensor psum, psq;\n if (training) {\n psum = torch::empty({C, 32}, dopts);\n psq = torch::empty({C, 32}, dopts);\n } else {\n psum = torch::empty({0}, dopts);\n psq = torch::empty({0}, dopts);\n }\n bn_scale_cuda(y, gamma.contiguous(), beta.contiguous(), running_mean, running_var,\n out, a, b, psum, psq, training, momentum, eps, scaling);\n return out;\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\nstatic constexpr int THREADS = 256;\nstatic constexpr int PARTS = 32;\n\n__global__ void partial_stats_kernel(const float* __restrict__ y,\n double* __restrict__ psum,\n double* __restrict__ psq,\n int N, int C, int H, int W) {\n int c = blockIdx.x;\n int part = blockIdx.y;\n int tid = threadIdx.x;\n long HW = (long)H * W;\n long M = (long)N * HW;\n double s = 0.0;\n double q = 0.0;\n for (long m = (long)part * blockDim.x + tid; m < M; m += (long)blockDim.x * gridDim.y) {\n long n = m / HW;\n long r = m - n * HW;\n float v = y[(n * C + c) * HW + r];\n s += (double)v;\n q += (double)v * (double)v;\n }\n extern __shared__ double sh[];\n double* ss = sh;\n double* qq = sh + blockDim.x;\n ss[tid] = s;\n qq[tid] = q;\n __syncthreads();\n for (int stride = blockDim.x >> 1; stride > 0; stride >>= 1) {\n if (tid < stride) {\n ss[tid] += ss[tid + stride];\n qq[tid] += qq[tid + stride];\n }\n __syncthreads();\n }\n if (tid == 0) {\n psum[c * PARTS + part] = ss[0];\n psq [c * PARTS + part] = qq[0];\n }\n}\n\n__global__ void finalize_train_kernel(const double* __restrict__ psum,\n const double* __restrict__ psq,\n const float* __restrict__ gamma,\n const float* __restrict__ beta,\n float* __restrict__ running_mean,\n float* __restrict__ running_var,\n float* __restrict__ a,\n float* __restrict__ b,\n int C, long M, double momentum,\n double eps, double scaling) {\n int c = blockIdx.x * blockDim.x + threadIdx.x;\n if (c >= C) return;\n double s = 0.0, q = 0.0;\n #pragma unroll\n for (int p = 0; p < PARTS; ++p) {\n s += psum[c * PARTS + p];\n q += psq [c * PARTS + p];\n }\n double mean = s / (double)M;\n double var = q / (double)M - mean * mean;\n if (var < 0.0) var = 0.0;\n double inv = rsqrt(var + eps);\n double g = (double)gamma[c];\n double be = (double)beta[c];\n double sc = scaling;\n a[c] = (float)(g * inv * sc);\n b[c] = (float)((be - mean * g * inv) * sc);\n\n // Match BatchNorm's running-stat update (unbiased variance for running_var).\n double unb = (M > 1) ? var * ((double)M / (double)(M - 1)) : var;\n running_mean[c] = (float)(((1.0 - momentum) * (double)running_mean[c]) + momentum * mean);\n running_var[c] = (float)(((1.0 - momentum) * (double)running_var[c]) + momentum * unb);\n}\n\n__global__ void coeff_eval_kernel(const float* __restrict__ gamma,\n const float* __restrict__ beta,\n const float* __restrict__ running_mean,\n const float* __restrict__ running_var,\n float* __restrict__ a,\n float* __restrict__ b,\n int C, double eps, double scaling) {\n int c = blockIdx.x * blockDim.x + threadIdx.x;\n if (c >= C) return;\n double inv = rsqrt((double)running_var[c] + eps);\n double g = (double)gamma[c];\n double sc = scaling;\n a[c] = (float)(g * inv * sc);\n b[c] = (float)((double)beta[c] * sc - (double)running_mean[c] * g * inv * sc);\n}\n\n__global__ void apply_affine_kernel(const float* __restrict__ y,\n const float* __restrict__ a,\n const float* __restrict__ b,\n float* __restrict__ out,\n long total, int C, int H, int W) {\n long idx = (long)blockIdx.x * blockDim.x + threadIdx.x;\n long stride = (long)blockDim.x * gridDim.x;\n long HW = (long)H * W;\n for (; idx < total; idx += stride) {\n int c = (int)((idx / HW) % C);\n out[idx] = y[idx] * a[c] + b[c];\n }\n}\n\nvoid bn_scale_cuda(torch::Tensor y, torch::Tensor gamma, torch::Tensor beta,\n torch::Tensor running_mean, torch::Tensor running_var,\n torch::Tensor out, torch::Tensor a, torch::Tensor b,\n torch::Tensor psum, torch::Tensor psq,\n bool training, double momentum, double eps, double scaling) {\n int N = (int)y.size(0);\n int C = (int)y.size(1);\n int H = (int)y.size(2);\n int W = (int)y.size(3);\n long total = (long)N * C * H * W;\n long M = (long)N * H * W;\n cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n if (training) {\n dim3 grid(C, PARTS);\n partial_stats_kernel<<<grid, THREADS, 2 * THREADS * sizeof(double), stream>>>(\n y.data_ptr<float>(), psum.data_ptr<double>(), psq.data_ptr<double>(), N, C, H, W);\n int blocksC = (C + 127) / 128;\n finalize_train_kernel<<<blocksC, 128, 0, stream>>>(\n psum.data_ptr<double>(), psq.data_ptr<double>(), gamma.data_ptr<float>(), beta.data_ptr<float>(),\n running_mean.data_ptr<float>(), running_var.data_ptr<float>(), a.data_ptr<float>(), b.data_ptr<float>(),\n C, M, momentum, eps, scaling);\n } else {\n int blocksC = (C + 127) / 128;\n coeff_eval_kernel<<<blocksC, 128, 0, stream>>>(\n gamma.data_ptr<float>(), beta.data_ptr<float>(), running_mean.data_ptr<float>(), running_var.data_ptr<float>(),\n a.data_ptr<float>(), b.data_ptr<float>(), C, eps, scaling);\n }\n int blocks = (int)((total + THREADS - 1) / THREADS);\n if (blocks > 65535) blocks = 65535;\n apply_affine_kernel<<<blocks, THREADS, 0, stream>>>(\n y.data_ptr<float>(), a.data_ptr<float>(), b.data_ptr<float>(), out.data_ptr<float>(), total, C, H, W);\n}\n'''\n\n_ext = load_inline(\n name=\"conv_bn_scale_fused_ext_v1\",\n cpp_sources=_cpp_src,\n cuda_sources=_cuda_src,\n functions=[\"bn_scale_forward\"],\n extra_cflags=[\"-O3\"],\n extra_cuda_cflags=[\"-O3\", \"--use_fast_math\"],\n verbose=False,\n)\n\nclass ModelNew(nn.Module):\n \"\"\"Conv2d followed by BatchNorm2d and a scalar multiply; BN affine+scale is fused.\"\"\"\n def __init__(self, in_channels, out_channels, kernel_size, scaling_factor):\n super(ModelNew, self).__init__()\n self.conv = nn.Conv2d(in_channels, out_channels, kernel_size)\n self.bn = nn.BatchNorm2d(out_channels)\n self.scaling_factor = scaling_factor\n\n def forward(self, x):\n y = self.conv(x)\n momentum = self.bn.momentum if self.bn.momentum is not None else 0.1\n if self.training and self.bn.track_running_stats:\n # Keep the observable BatchNorm buffer behavior consistent with PyTorch.\n self.bn.num_batches_tracked.add_(1)\n if self.bn.momentum is None:\n momentum = 1.0 / float(self.bn.num_batches_tracked.item())\n return _ext.bn_scale_forward(\n y, self.bn.weight, self.bn.bias, self.bn.running_mean, self.bn.running_var,\n bool(self.training), float(momentum), float(self.bn.eps), float(self.scaling_factor)\n )\n"
}