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 bn_forward(torch::Tensor x,\n torch::Tensor weight,\n torch::Tensor bias,\n torch::Tensor running_mean,\n torch::Tensor running_var,\n bool training);\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#ifndef __CUDACC_RTC__\n#include <cstdio>\n#endif\n\nstatic __device__ __forceinline__ void warp_reduce_pair(float &a, float &b) {\n #pragma unroll\n for (int offset = 16; offset > 0; offset >>= 1) {\n a += __shfl_down_sync(0xffffffff, a, offset);\n b += __shfl_down_sync(0xffffffff, b, offset);\n }\n}\n\n__global__ void reduce_bn_kernel(const float* __restrict__ x,\n float* __restrict__ partial,\n long long N, int C, long long HW,\n int chunks_per_n, long long chunk_size,\n int chunks_per_c) {\n int bid = blockIdx.x;\n int c = bid / chunks_per_c;\n int k = bid - c * chunks_per_c;\n long long n = k / chunks_per_n;\n int seg = k - (int)(n * chunks_per_n);\n long long start = (long long)seg * chunk_size;\n long long len = chunk_size;\n if (start + len > HW) len = HW - start;\n const float* ptr = x + ((n * C + c) * HW + start);\n\n float sum = 0.0f;\n float sum2 = 0.0f;\n for (long long i = threadIdx.x; i < len; i += blockDim.x) {\n float v = ptr[i];\n sum += v;\n sum2 = fmaf(v, v, sum2);\n }\n\n warp_reduce_pair(sum, sum2);\n\n __shared__ float smem_sum[32];\n __shared__ float smem_sum2[32];\n int lane = threadIdx.x & 31;\n int warp = threadIdx.x >> 5;\n if (lane == 0) {\n smem_sum[warp] = sum;\n smem_sum2[warp] = sum2;\n }\n __syncthreads();\n\n if (warp == 0) {\n sum = (lane < (blockDim.x >> 5)) ? smem_sum[lane] : 0.0f;\n sum2 = (lane < (blockDim.x >> 5)) ? smem_sum2[lane] : 0.0f;\n warp_reduce_pair(sum, sum2);\n if (lane == 0) {\n long long out = ((long long)c * chunks_per_c + k) * 2LL;\n partial[out] = sum;\n partial[out + 1] = sum2;\n }\n }\n}\n\n__global__ void finalize_bn_kernel(const float* __restrict__ partial,\n float* __restrict__ stats,\n float* __restrict__ running_mean,\n float* __restrict__ running_var,\n int C, int chunks_per_c, long long M,\n float eps, float momentum) {\n int c = blockIdx.x;\n float sum = 0.0f;\n float sum2 = 0.0f;\n for (int k = threadIdx.x; k < chunks_per_c; k += blockDim.x) {\n long long idx = ((long long)c * chunks_per_c + k) * 2LL;\n sum += partial[idx];\n sum2 += partial[idx + 1];\n }\n\n warp_reduce_pair(sum, sum2);\n\n __shared__ float smem_sum[32];\n __shared__ float smem_sum2[32];\n int lane = threadIdx.x & 31;\n int warp = threadIdx.x >> 5;\n if (lane == 0) {\n smem_sum[warp] = sum;\n smem_sum2[warp] = sum2;\n }\n __syncthreads();\n\n if (warp == 0) {\n sum = (lane < (blockDim.x >> 5)) ? smem_sum[lane] : 0.0f;\n sum2 = (lane < (blockDim.x >> 5)) ? smem_sum2[lane] : 0.0f;\n warp_reduce_pair(sum, sum2);\n if (lane == 0) {\n float invM = 1.0f / (float)M;\n float mean = sum * invM;\n float var = sum2 * invM - mean * mean;\n var = fmaxf(var, 0.0f);\n float invstd = rsqrtf(var + eps);\n stats[2 * c] = mean;\n stats[2 * c + 1] = invstd;\n\n float old_m = running_mean[c];\n float old_v = running_var[c];\n float unbiased = var * ((float)M / (float)(M - 1));\n running_mean[c] = old_m * (1.0f - momentum) + mean * momentum;\n running_var[c] = old_v * (1.0f - momentum) + unbiased * momentum;\n }\n }\n}\n\n__global__ void norm_train_kernel(const float* __restrict__ x,\n const float* __restrict__ weight,\n const float* __restrict__ bias,\n const float* __restrict__ stats,\n float* __restrict__ y,\n long long N, int C, long long HW) {\n int bc = blockIdx.x;\n int c = bc % C;\n long long n = bc / C;\n long long base = (n * C + c) * HW;\n float mean = stats[2 * c];\n float invstd = stats[2 * c + 1];\n float gamma = weight[c];\n float beta = bias[c];\n float scale = gamma * invstd;\n float shift = beta - mean * scale;\n for (long long s = threadIdx.x; s < HW; s += blockDim.x) {\n y[base + s] = fmaf(x[base + s], scale, shift);\n }\n}\n\n__global__ void norm_eval_kernel(const float* __restrict__ x,\n const float* __restrict__ weight,\n const float* __restrict__ bias,\n const float* __restrict__ running_mean,\n const float* __restrict__ running_var,\n float* __restrict__ y,\n long long N, int C, long long HW, float eps) {\n int bc = blockIdx.x;\n int c = bc % C;\n long long n = bc / C;\n long long base = (n * C + c) * HW;\n float invstd = rsqrtf(running_var[c] + eps);\n float scale = weight[c] * invstd;\n float shift = bias[c] - running_mean[c] * scale;\n for (long long s = threadIdx.x; s < HW; s += blockDim.x) {\n y[base + s] = fmaf(x[base + s], scale, shift);\n }\n}\n\ntorch::Tensor bn_forward(torch::Tensor x,\n torch::Tensor weight,\n torch::Tensor bias,\n torch::Tensor running_mean,\n torch::Tensor running_var,\n bool training) {\n TORCH_CHECK(x.is_cuda(), \"x must be CUDA\");\n TORCH_CHECK(x.scalar_type() == at::kFloat, \"only float32 supported\");\n TORCH_CHECK(x.dim() == 4, \"expected NCHW tensor\");\n TORCH_CHECK(x.is_contiguous(), \"x must be contiguous\");\n\n const long long N = x.size(0);\n const int C = (int)x.size(1);\n const long long H = x.size(2);\n const long long W = x.size(3);\n const long long HW = H * W;\n auto y = torch::empty_like(x);\n\n const int threads = 256;\n const float eps = 1.0e-5f;\n cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n\n if (!training) {\n dim3 grid((unsigned int)(N * C));\n norm_eval_kernel<<<grid, threads, 0, stream>>>(\n x.data_ptr<float>(), weight.data_ptr<float>(), bias.data_ptr<float>(),\n running_mean.data_ptr<float>(), running_var.data_ptr<float>(),\n y.data_ptr<float>(), N, C, HW, eps);\n return y;\n }\n\n // 64k spatial elements per partial block. For the benchmark shape this is\n // exactly four chunks per image/channel, giving 16k independent reductions.\n const long long chunk_size = 65536LL;\n const int chunks_per_n = (int)((HW + chunk_size - 1) / chunk_size);\n const int chunks_per_c = (int)(N * (long long)chunks_per_n);\n\n auto partial = torch::empty({C, chunks_per_c, 2}, x.options());\n auto stats = torch::empty({C, 2}, x.options());\n\n dim3 red_grid((unsigned int)(C * chunks_per_c));\n reduce_bn_kernel<<<red_grid, threads, 0, stream>>>(\n x.data_ptr<float>(), partial.data_ptr<float>(), N, C, HW,\n chunks_per_n, chunk_size, chunks_per_c);\n\n finalize_bn_kernel<<<C, threads, 0, stream>>>(\n partial.data_ptr<float>(), stats.data_ptr<float>(),\n running_mean.data_ptr<float>(), running_var.data_ptr<float>(),\n C, chunks_per_c, N * HW, eps, 0.1f);\n\n dim3 norm_grid((unsigned int)(N * C));\n norm_train_kernel<<<norm_grid, threads, 0, stream>>>(\n x.data_ptr<float>(), weight.data_ptr<float>(), bias.data_ptr<float>(),\n stats.data_ptr<float>(), y.data_ptr<float>(), N, C, HW);\n\n return y;\n}\n\"\"\"\n\n_bn_ext = load_inline(\n name=\"bn2d_forward_ext_v1\",\n cpp_sources=_cpp_src,\n cuda_sources=_cuda_src,\n functions=[\"bn_forward\"],\n extra_cflags=[\"-O3\"],\n extra_cuda_cflags=[\"-O3\"],\n verbose=False,\n)\n\nclass ModelNew(nn.Module):\n def __init__(self, num_features: int):\n super(ModelNew, self).__init__()\n self.bn = nn.BatchNorm2d(num_features=num_features)\n\n def forward(self, x: torch.Tensor) -> torch.Tensor:\n if self.bn.training:\n # Match BatchNorm2d's bookkeeping for the default track_running_stats=True.\n self.bn.num_batches_tracked.add_(1)\n return _bn_ext.bn_forward(x, self.bn.weight, self.bn.bias,\n self.bn.running_mean, self.bn.running_var,\n bool(self.bn.training))\n"
}