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 bn_gelu_relu_forward(torch::Tensor x,\n torch::Tensor gamma,\n torch::Tensor beta,\n torch::Tensor running_mean,\n torch::Tensor running_var,\n bool training,\n double eps,\n double momentum);\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 <math.h>\n\n#define COL_TILE 32\n#define ROW_TILE 16\n#define Y_THREADS 8\n\n__global__ void partial_sum_kernel(const float* __restrict__ x,\n float* __restrict__ partial,\n float* __restrict__ partial2,\n int N, int C, int tiles) {\n int col0 = blockIdx.x * COL_TILE;\n int tile = blockIdx.y;\n int tx = threadIdx.x; // 0..31, feature within tile\n int ty = threadIdx.y; // 0..7, row reducer\n int c = col0 + tx;\n int row0 = tile * ROW_TILE;\n\n float s = 0.0f;\n float q = 0.0f;\n if (c < C) {\n #pragma unroll\n for (int r = ty; r < ROW_TILE; r += Y_THREADS) {\n int n = row0 + r;\n if (n < N) {\n float v = x[(long long)n * C + c];\n s += v;\n q += v * v;\n }\n }\n }\n\n __shared__ float sh[Y_THREADS][COL_TILE];\n __shared__ float sh2[Y_THREADS][COL_TILE];\n sh[ty][tx] = s;\n sh2[ty][tx] = q;\n __syncthreads();\n\n if (ty == 0 && c < C) {\n float ss = 0.0f;\n float qq = 0.0f;\n #pragma unroll\n for (int k = 0; k < Y_THREADS; ++k) {\n ss += sh[k][tx];\n qq += sh2[k][tx];\n }\n partial[(long long)tile * C + c] = ss;\n partial2[(long long)tile * C + c] = qq;\n }\n}\n\n__global__ void finalize_stats_kernel(const float* __restrict__ partial,\n const float* __restrict__ partial2,\n float* __restrict__ mean,\n float* __restrict__ invstd,\n float* __restrict__ running_mean,\n float* __restrict__ running_var,\n int N, int C, int tiles,\n float eps, float momentum) {\n int c = blockIdx.x;\n int tid = threadIdx.x;\n float s = 0.0f;\n float q = 0.0f;\n for (int t = tid; t < tiles; t += blockDim.x) {\n float a = partial[(long long)t * C + c];\n float b = partial2[(long long)t * C + c];\n s += a;\n q += b;\n }\n\n __shared__ float ss[256];\n __shared__ float qq[256];\n ss[tid] = s;\n qq[tid] = q;\n __syncthreads();\n\n for (int stride = 128; 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\n if (tid == 0) {\n float n_inv = 1.0f / (float)N;\n float m = ss[0] * n_inv;\n float var = qq[0] * n_inv - m * m;\n var = var < 0.0f ? 0.0f : var;\n float inv = rsqrtf(var + eps);\n mean[c] = m;\n invstd[c] = inv;\n // PyTorch BatchNorm updates running_var with the unbiased variance.\n float unb = (N > 1) ? (var * ((float)N / (float)(N - 1))) : var;\n running_mean[c] = running_mean[c] * (1.0f - momentum) + m * momentum;\n running_var[c] = running_var[c] * (1.0f - momentum) + unb * momentum;\n }\n}\n\n__device__ __forceinline__ float gelu_relu_exact(float z) {\n if (z <= 0.0f) return 0.0f;\n const float inv_sqrt2 = 0.70710678118654752440f;\n return 0.5f * z * (1.0f + erff(z * inv_sqrt2));\n}\n\n__global__ void norm_act_train_kernel(float* __restrict__ x,\n const float* __restrict__ mean,\n const float* __restrict__ invstd,\n const float* __restrict__ gamma,\n const float* __restrict__ beta,\n long long total, int C) {\n long long idx = (long long)blockIdx.x * blockDim.x + threadIdx.x;\n long long stride = (long long)blockDim.x * gridDim.x;\n for (; idx < total; idx += stride) {\n int c = (int)(idx % C);\n float z = (x[idx] - mean[c]) * invstd[c];\n z = z * gamma[c] + beta[c];\n x[idx] = gelu_relu_exact(z);\n }\n}\n\n__global__ void norm_act_eval_kernel(float* __restrict__ x,\n const float* __restrict__ running_mean,\n const float* __restrict__ running_var,\n const float* __restrict__ gamma,\n const float* __restrict__ beta,\n long long total, int C, float eps) {\n long long idx = (long long)blockIdx.x * blockDim.x + threadIdx.x;\n long long stride = (long long)blockDim.x * gridDim.x;\n for (; idx < total; idx += stride) {\n int c = (int)(idx % C);\n float z = (x[idx] - running_mean[c]) * rsqrtf(running_var[c] + eps);\n z = z * gamma[c] + beta[c];\n x[idx] = gelu_relu_exact(z);\n }\n}\n\ntorch::Tensor bn_gelu_relu_forward(torch::Tensor x,\n torch::Tensor gamma,\n torch::Tensor beta,\n torch::Tensor running_mean,\n torch::Tensor running_var,\n bool training,\n double eps,\n double momentum) {\n TORCH_CHECK(x.is_cuda(), \"x must be CUDA\");\n TORCH_CHECK(x.scalar_type() == torch::kFloat32, \"only float32 supported\");\n TORCH_CHECK(x.dim() == 2, \"x must be 2D\");\n const int N = (int)x.size(0);\n const int C = (int)x.size(1);\n const long long total = (long long)N * C;\n cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n\n const int threads = 256;\n int blocks = (int)((total + threads - 1) / threads);\n if (blocks > 65535) blocks = 65535;\n\n if (training) {\n int tiles = (N + ROW_TILE - 1) / ROW_TILE;\n auto opts = x.options();\n torch::Tensor partial = torch::empty({tiles, C}, opts);\n torch::Tensor partial2 = torch::empty({tiles, C}, opts);\n torch::Tensor mean = torch::empty({C}, opts);\n torch::Tensor invstd = torch::empty({C}, opts);\n\n dim3 block(COL_TILE, Y_THREADS);\n dim3 grid((C + COL_TILE - 1) / COL_TILE, tiles);\n partial_sum_kernel<<<grid, block, 0, stream>>>(\n x.data_ptr<float>(), partial.data_ptr<float>(), partial2.data_ptr<float>(), N, C, tiles);\n finalize_stats_kernel<<<C, 256, 0, stream>>>(\n partial.data_ptr<float>(), partial2.data_ptr<float>(), mean.data_ptr<float>(), invstd.data_ptr<float>(),\n running_mean.data_ptr<float>(), running_var.data_ptr<float>(), N, C, tiles, (float)eps, (float)momentum);\n norm_act_train_kernel<<<blocks, threads, 0, stream>>>(\n x.data_ptr<float>(), mean.data_ptr<float>(), invstd.data_ptr<float>(), gamma.data_ptr<float>(), beta.data_ptr<float>(), total, C);\n } else {\n norm_act_eval_kernel<<<blocks, threads, 0, stream>>>(\n x.data_ptr<float>(), running_mean.data_ptr<float>(), running_var.data_ptr<float>(), gamma.data_ptr<float>(), beta.data_ptr<float>(), total, C, (float)eps);\n }\n return x;\n}\n\"\"\"\n\n_ext = load_inline(\n name=\"bn_gelu_relu_ext_v1\",\n cpp_sources=[_cpp_src],\n cuda_sources=[_cuda_src],\n functions=[\"bn_gelu_relu_forward\"],\n extra_cflags=[\"-O3\"],\n extra_cuda_cflags=[\"-O3\"],\n with_cuda=True,\n verbose=False,\n)\n\n\nclass ModelNew(nn.Module):\n \"\"\"GEMM + BatchNorm1d + GELU + ReLU, with fused CUDA post-processing.\"\"\"\n def __init__(self, in_features, out_features):\n super(ModelNew, self).__init__()\n self.gemm = nn.Linear(in_features, out_features)\n self.batch_norm = nn.BatchNorm1d(out_features)\n\n def forward(self, x):\n y = F.linear(x, self.gemm.weight, self.gemm.bias)\n bn = self.batch_norm\n if self.training and bn.track_running_stats:\n # Match BatchNorm's bookkeeping for fixed momentum (the default used here).\n bn.num_batches_tracked.add_(1)\n return _ext.bn_gelu_relu_forward(\n y,\n bn.weight,\n bn.bias,\n bn.running_mean,\n bn.running_var,\n bool(self.training),\n float(bn.eps),\n float(bn.momentum if bn.momentum is not None else 0.0),\n )\n"
}