gpt-5.5-priority · original · l12_all_gpt
correct 39_Gemm_Scale_BatchNorm.py
model
gpt-5.5-priority
turns / max
9 / 15
tool calls
9
tokens (total)
0
wall clock
—
truncated
no
backend
cuda fp32
variant
original
runtime
11.30 μs
ref runtime
11.50 μs
speedup
1.02x
started
2026-05-04T07:52:23Z
timingkernelbench.timing
kernel runtime
11.30 μs
ref runtime
11.50 μs
speedup
1.02x
kernel mean / median / std
11.30 / — / 0.20 μs
ref mean / median / std
11.50 / — / 0.31 μs
numerical precisioneval_kernel_against_ref
max abs error
7.30e-05
mean abs error
2.32e-06
max rel error
1.25e+07
mean rel error
0.20
gpu memoryextended_metrics.measure_memory
kernel peak
739.20 MB
ref peak
933.22 MB
kernel / ref
0.79x
kernel launches / fusiontorch.profiler
kernel launches
10
ref launches
20
fusion ratio (ref / kernel)
2.00x
total cuda time
10899 μs
energyNVML (pynvml)
kernel mJ / run
1450.62
ref mJ / run
1209.68
ratio (ref / kernel)
0.83x
avg power
129.09 W
ref avg power
104.84 W
SOL (speed-of-light)nsight_hardware_counters
SOL score (0–1)
1.000
dram util %
100.0%
compute util %
100.0%
bottleneck
compute-bound
dominant pipe
FP32
arithmetic intensity
—
ridge point
20.00
roofline / occupancynsight_hardware_counters
dram bandwidth
7592.55 GB/s
dram util %
100.0%
fp32 tflops
—
fp32 util %
—
fp16 tflops
—
occupancy %
291.3%
bottleneck
compute-bound
dominant pipe %
104.9%
arithmetic intensity
—
ridge point
20.00
L1 hit %
—
L2 hit %
179.0%
ld sectors / req
14.50
st sectors / req
—
regs / thread
344
smem / block
40320 B
block size
896
peak bw
3350.00 GB/s
peak fp32 tflops
67.00
peak fp16 tflops
1979.00
warp stalls (top 5)nsight
long_scoreboard
23172.4%
short_scoreboard
169.3%
mio_throttle
57.4%
reference PyTorch source (the prompt the agent sees)
import torch
import torch.nn as nn

class Model(nn.Module):
    """
    Simple model that performs a matrix multiplication, scales the result, and applies batch normalization.
    """
    def __init__(self, in_features, out_features, scale_shape, eps=1e-5, momentum=0.1):
        super(Model, self).__init__()
        self.gemm = nn.Linear(in_features, out_features)
        self.scale = nn.Parameter(torch.randn(scale_shape))
        self.bn = nn.BatchNorm1d(out_features, eps=eps, momentum=momentum)

    def forward(self, x):
        x = self.gemm(x)
        x = x * self.scale
        x = self.bn(x)
        return x

batch_size = 16384
in_features = 4096
out_features = 4096
scale_shape = (out_features,)

def get_inputs():
    return [torch.rand(batch_size, in_features)]

def get_init_inputs():
    return [in_features, out_features, scale_shape]
turn 0 · 1 tool call(s) · 50.2s turn · 50.2s LLM · 6,107 chars (click to expand)
turn 050.2s turn · 50.2s LLM · 0.0s tools
reasoning
**Evaluating ModelNew implementation** I need to craft ModelNew and consider whether to infer eval training. The default BatchNorm1d is True, but I'm thinking about the best way to manage weight copies. I'll also implement the forward pass and ensure to include parameter names for state_dict. ModelNew should handle operations with matmul and scaling, and I'll need to benchmark it against references. For BN in training, it computes per-batch statistics. I want to ensure the output is correct in both training and evaluation modes. **Exploring GEMM implementation options** I'm working on implementing a 16k x 4k GEMM, which is quite large and around 137 GFLOP. The PyTorch reference uses cuBLAS, along with elementwise scaling and batch normalization across multiple passes. I’m considering whether I could replace cuBLAS with a custom kernel, but it might be challenging. I could use torch operations and possibly call at::matmul. To be faster than the reference, I might fuse scaling into batch normalization, computing GEMM via torch linear first. I need to ensure that this approach can outperform cuBLAS. I'll also consider leveraging cuBLAS from a C++ wrapper for GEMM while implementing custom batch normalization. **Implementing optimizations for performance** The reference uses PyTorch’s Linear, multiplication, and BatchNorm. I’m thinking that extension call overhead might be lower, especially if I can fuse scaling into reductions or outputs for potential speed gains. I definitely need to implement this! Alternatively, I could explore the idea of exploiting BN by setting it up as BN(x*scale) = BN(z) with sign scaling, which might offer another performance boost. I need to figure out which approach works best!
call → compile_kernel
callcompile_kernel { "kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\ncpp_source = r'''\n#include <torch/extension.h>\n\nvoid scale_weight_bias_cuda(torch::Tensor weight, torch::Tensor bias, torch::Tensor scale, torch::Tensor sw, torch::Tensor sb);\n\ntorch::Tensor forward(torch::Tensor x, torch::Tensor weight, torch::Tensor bias, torch::Tensor scale,\n torch::Tensor bn_weight, torch::Tensor bn_bias,\n torch::Tensor running_mean, torch::Tensor running_var,\n bool training, double eps, double momentum) {\n at::NoGradGuard no_grad;\n auto sw = torch::empty_like(weight);\n auto sb = torch::empty_like(bias);\n scale_weight_bias_cuda(weight, bias, scale, sw, sb);\n auto y = torch::addmm(sb, x, sw.t());\n auto out = torch::batch_norm(y, bn_weight, bn_bias, running_mean, running_var,\n training, momentum, eps, true);\n return out;\n}\n'''\n\ncuda_source = r'''\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n\ntemplate <typename scalar_t>\n__global__ void scale_weight_bias_kernel(const scalar_t* __restrict__ w,\n const scalar_t* __restrict__ b,\n const scalar_t* __restrict__ s,\n scalar_t* __restrict__ sw,\n scalar_t* __restrict__ sb,\n int out_features, int in_features, long total) {\n long idx = blockIdx.x * blockDim.x + threadIdx.x;\n long stride = (long)blockDim.x * gridDim.x;\n for (long i = idx; i < total; i += stride) {\n int o = (int)(i / in_features);\n sw[i] = w[i] * s[o];\n }\n for (int o = idx; o < out_features; o += stride) {\n sb[o] = b[o] * s[o];\n }\n}\n\nvoid scale_weight_bias_cuda(torch::Tensor weight, torch::Tensor bias, torch::Tensor scale, torch::Tensor sw, torch::Tensor sb) {\n int out_features = weight.size(0);\n int in_features = weight.size(1);\n long total = (long)out_features * (long)in_features;\n int threads = 256;\n int blocks = (int)((total + threads - 1) / threads);\n if (blocks > 4096) blocks = 4096;\n AT_DISPATCH_FLOATING_TYPES(weight.scalar_type(), \"scale_weight_bias_cuda\", [&] {\n scale_weight_bias_kernel<scalar_t><<<blocks, threads>>>(\n weight.data_ptr<scalar_t>(), bias.data_ptr<scalar_t>(), scale.data_ptr<scalar_t>(),\n sw.data_ptr<scalar_t>(), sb.data_ptr<scalar_t>(), out_features, in_features, total);\n });\n}\n'''\n\n_ext = load_inline(name='fused_linear_scale_bn_ext_v1', cpp_sources=cpp_source, cuda_sources=cuda_source,\n functions=['forward'], extra_cflags=['-O3'], extra_cuda_cflags=['-O3'], verbose=False)\n\nclass ModelNew(nn.Module):\n def __init__(self, in_features, out_features, scale_shape, eps=1e-5, momentum=0.1):\n super(ModelNew, self).__init__()\n self.gemm = nn.Linear(in_features, out_features)\n self.scale = nn.Parameter(torch.randn(scale_shape))\n self.bn = nn.BatchNorm1d(out_features, eps=eps, momentum=momentum)\n def forward(self, x):\n return _ext.forward(x, self.gemm.weight, self.gemm.bias, self.scale,\n self.bn.weight, self.bn.bias, self.bn.running_mean, self.bn.running_var,\n self.training, float(self.bn.eps), float(self.bn.momentum))\n" }
result ← compile_kernel (ok)
okcompile_kernel compile_kernel PASSED: kernel compiled without errors.
turn 18.1s turn · 8.1s LLM · 0.0s tools
call → run_correctness
callrun_correctness { "kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\ncpp_source = r'''\n#include <torch/extension.h>\n\nvoid scale_weight_bias_cuda(torch::Tensor weight, torch::Tensor bias, torch::Tensor scale, torch::Tensor sw, torch::Tensor sb);\n\ntorch::Tensor forward(torch::Tensor x, torch::Tensor weight, torch::Tensor bias, torch::Tensor scale,\n torch::Tensor bn_weight, torch::Tensor bn_bias,\n torch::Tensor running_mean, torch::Tensor running_var,\n bool training, double eps, double momentum) {\n at::NoGradGuard no_grad;\n auto sw = torch::empty_like(weight);\n auto sb = torch::empty_like(bias);\n scale_weight_bias_cuda(weight, bias, scale, sw, sb);\n auto y = torch::addmm(sb, x, sw.t());\n auto out = torch::batch_norm(y, bn_weight, bn_bias, running_mean, running_var,\n training, momentum, eps, true);\n return out;\n}\n'''\n\ncuda_source = r'''\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n\ntemplate <typename scalar_t>\n__global__ void scale_weight_bias_kernel(const scalar_t* __restrict__ w,\n const scalar_t* __restrict__ b,\n const scalar_t* __restrict__ s,\n scalar_t* __restrict__ sw,\n scalar_t* __restrict__ sb,\n int out_features, int in_features, long total) {\n long idx = blockIdx.x * blockDim.x + threadIdx.x;\n long stride = (long)blockDim.x * gridDim.x;\n for (long i = idx; i < total; i += stride) {\n int o = (int)(i / in_features);\n sw[i] = w[i] * s[o];\n }\n for (int o = idx; o < out_features; o += stride) {\n sb[o] = b[o] * s[o];\n }\n}\n\nvoid scale_weight_bias_cuda(torch::Tensor weight, torch::Tensor bias, torch::Tensor scale, torch::Tensor sw, torch::Tensor sb) {\n int out_features = weight.size(0);\n int in_features = weight.size(1);\n long total = (long)out_features * (long)in_features;\n int threads = 256;\n int blocks = (int)((total + threads - 1) / threads);\n if (blocks > 4096) blocks = 4096;\n AT_DISPATCH_FLOATING_TYPES(weight.scalar_type(), \"scale_weight_bias_cuda\", [&] {\n scale_weight_bias_kernel<scalar_t><<<blocks, threads>>>(\n weight.data_ptr<scalar_t>(), bias.data_ptr<scalar_t>(), scale.data_ptr<scalar_t>(),\n sw.data_ptr<scalar_t>(), sb.data_ptr<scalar_t>(), out_features, in_features, total);\n });\n}\n'''\n\n_ext = load_inline(name='fused_linear_scale_bn_ext_v1', cpp_sources=cpp_source, cuda_sources=cuda_source,\n functions=['forward'], extra_cflags=['-O3'], extra_cuda_cflags=['-O3'], verbose=False)\n\nclass ModelNew(nn.Module):\n def __init__(self, in_features, out_features, scale_shape, eps=1e-5, momentum=0.1):\n super(ModelNew, self).__init__()\n self.gemm = nn.Linear(in_features, out_features)\n self.scale = nn.Parameter(torch.randn(scale_shape))\n self.bn = nn.BatchNorm1d(out_features, eps=eps, momentum=momentum)\n def forward(self, x):\n return _ext.forward(x, self.gemm.weight, self.gemm.bias, self.scale,\n self.bn.weight, self.bn.bias, self.bn.running_mean, self.bn.running_var,\n self.training, float(self.bn.eps), float(self.bn.momentum))\n" }
result ← run_correctness (ok)
okrun_correctness run_correctness PASSED: (5 / 5) trials all matched the reference. Numerical precision: max_abs_err=5.52e-05 mean_abs_err=2.26e-06 max_rel_err=1.35e+07
turn 26.9s turn · 6.9s LLM · 0.0s tools
call → profile_kernel
callprofile_kernel { "kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\ncpp_source = r'''\n#include <torch/extension.h>\n\nvoid scale_weight_bias_cuda(torch::Tensor weight, torch::Tensor bias, torch::Tensor scale, torch::Tensor sw, torch::Tensor sb);\n\ntorch::Tensor forward(torch::Tensor x, torch::Tensor weight, torch::Tensor bias, torch::Tensor scale,\n torch::Tensor bn_weight, torch::Tensor bn_bias,\n torch::Tensor running_mean, torch::Tensor running_var,\n bool training, double eps, double momentum) {\n at::NoGradGuard no_grad;\n auto sw = torch::empty_like(weight);\n auto sb = torch::empty_like(bias);\n scale_weight_bias_cuda(weight, bias, scale, sw, sb);\n auto y = torch::addmm(sb, x, sw.t());\n auto out = torch::batch_norm(y, bn_weight, bn_bias, running_mean, running_var,\n training, momentum, eps, true);\n return out;\n}\n'''\n\ncuda_source = r'''\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n\ntemplate <typename scalar_t>\n__global__ void scale_weight_bias_kernel(const scalar_t* __restrict__ w,\n const scalar_t* __restrict__ b,\n const scalar_t* __restrict__ s,\n scalar_t* __restrict__ sw,\n scalar_t* __restrict__ sb,\n int out_features, int in_features, long total) {\n long idx = blockIdx.x * blockDim.x + threadIdx.x;\n long stride = (long)blockDim.x * gridDim.x;\n for (long i = idx; i < total; i += stride) {\n int o = (int)(i / in_features);\n sw[i] = w[i] * s[o];\n }\n for (int o = idx; o < out_features; o += stride) {\n sb[o] = b[o] * s[o];\n }\n}\n\nvoid scale_weight_bias_cuda(torch::Tensor weight, torch::Tensor bias, torch::Tensor scale, torch::Tensor sw, torch::Tensor sb) {\n int out_features = weight.size(0);\n int in_features = weight.size(1);\n long total = (long)out_features * (long)in_features;\n int threads = 256;\n int blocks = (int)((total + threads - 1) / threads);\n if (blocks > 4096) blocks = 4096;\n AT_DISPATCH_FLOATING_TYPES(weight.scalar_type(), \"scale_weight_bias_cuda\", [&] {\n scale_weight_bias_kernel<scalar_t><<<blocks, threads>>>(\n weight.data_ptr<scalar_t>(), bias.data_ptr<scalar_t>(), scale.data_ptr<scalar_t>(),\n sw.data_ptr<scalar_t>(), sb.data_ptr<scalar_t>(), out_features, in_features, total);\n });\n}\n'''\n\n_ext = load_inline(name='fused_linear_scale_bn_ext_v1', cpp_sources=cpp_source, cuda_sources=cuda_source,\n functions=['forward'], extra_cflags=['-O3'], extra_cuda_cflags=['-O3'], verbose=False)\n\nclass ModelNew(nn.Module):\n def __init__(self, in_features, out_features, scale_shape, eps=1e-5, momentum=0.1):\n super(ModelNew, self).__init__()\n self.gemm = nn.Linear(in_features, out_features)\n self.scale = nn.Parameter(torch.randn(scale_shape))\n self.bn = nn.BatchNorm1d(out_features, eps=eps, momentum=momentum)\n def forward(self, x):\n return _ext.forward(x, self.gemm.weight, self.gemm.bias, self.scale,\n self.bn.weight, self.bn.bias, self.bn.running_mean, self.bn.running_var,\n self.training, float(self.bn.eps), float(self.bn.momentum))\n" }
result ← profile_kernel (ok)
okprofile_kernel profile_kernel PASSED: profiling complete. === Kernel Profile Summary === --- Kernel Breakdown (by GPU time) --- #1: aten::addmm 79732.0μs (64%) calls=1 #2: aten::batch_norm 11578.0μs (9%) calls=1 #3: aten::_batch_norm_impl_index 11572.0μs (9%) calls=1 #4: aten::native_batch_norm 11560.0μs (9%) calls=1 #5: aten::zeros 2889.0μs (2%) calls=1 ... +16 more kernels --- Timing --- GPU time: 11180.48 μs --- Memory Bandwidth --- DRAM BW: 7230.9/3350 GB/s (100.0%) read=4990.2 write=2240.7 GB/s Hit rates: L2=386.6% --- Compute Throughput --- Dominant pipe: FP32 util=136.5% Pipe util: FMA=136.5%, Tensor=3.3% --- Roofline Analysis --- Ridge point (FP32): 20.00 FLOPs/byte Classification: COMPUTE-BOUND --- Occupancy --- Achieved: 272.2% Eligible warps/cycle: 5.10 Launch: regs/thread=430, smem/blk=46208B, block=1664, grid=24581 --- Memory Access Quality --- Global ld coalescing: 18.50 sectors/req (18.5x wasted BW) --- Warp Stall Reasons (% of issue cycles) --- long_scoreboard 9367.6% #################### short_scoreboard 747.1% #################### mio_throttle 61.7% ############ --- Optimization Hints --- -> 9368% stalls: global memory latency (waiting for DRAM/L2). + sectors/req=18.5 -> uncoalesced global loads, restructure access pattern or use __ldg Try shared-memory tiling, prefetching, or reducing working set. -> Global load coalescing=18.5x (ideal=1.0) -> 19x wasted DRAM BW. Make per-warp accesses contiguous. -> Compute-bound but NOT using tensor cores. If applicable, use FP16/BF16 with torch.matmul or WMMA intrinsics.
turn 3 · 1 tool call(s) · 43.6s turn · 43.6s LLM · 10,083 chars (click to expand)
turn 343.6s turn · 43.6s LLM · 0.0s tools
call → compile_kernel
callcompile_kernel { "kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\ncpp_source = r'''\n#include <torch/extension.h>\n\nvoid scale_weight_bias_cuda(torch::Tensor weight, torch::Tensor bias, torch::Tensor scale, torch::Tensor sw, torch::Tensor sb);\nvoid bn_train_cuda(torch::Tensor y, torch::Tensor bn_weight, torch::Tensor bn_bias, torch::Tensor running_mean, torch::Tensor running_var, double eps, double momentum);\nvoid bn_eval_cuda(torch::Tensor y, torch::Tensor bn_weight, torch::Tensor bn_bias, torch::Tensor running_mean, torch::Tensor running_var, double eps);\n\ntorch::Tensor forward(torch::Tensor x, torch::Tensor weight, torch::Tensor bias, torch::Tensor scale,\n torch::Tensor bn_weight, torch::Tensor bn_bias,\n torch::Tensor running_mean, torch::Tensor running_var,\n bool training, double eps, double momentum) {\n at::NoGradGuard no_grad;\n auto sw = torch::empty_like(weight);\n auto sb = torch::empty_like(bias);\n scale_weight_bias_cuda(weight, bias, scale, sw, sb);\n auto y = torch::addmm(sb, x, sw.t());\n if (training) bn_train_cuda(y, bn_weight, bn_bias, running_mean, running_var, eps, momentum);\n else bn_eval_cuda(y, bn_weight, bn_bias, running_mean, running_var, eps);\n return y;\n}\n'''\n\ncuda_source = r'''\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <math.h>\n\ntemplate <typename scalar_t>\n__global__ void scale_weight_bias_kernel(const scalar_t* __restrict__ w, const scalar_t* __restrict__ b,\n const scalar_t* __restrict__ s, scalar_t* __restrict__ sw,\n scalar_t* __restrict__ sb, int C, int K, long total) {\n long idx = blockIdx.x * blockDim.x + threadIdx.x;\n long stride = (long)blockDim.x * gridDim.x;\n for (long i = idx; i < total; i += stride) {\n int o = (int)(i / K);\n sw[i] = w[i] * s[o];\n }\n for (int o = idx; o < C; o += stride) sb[o] = b[o] * s[o];\n}\n\nvoid scale_weight_bias_cuda(torch::Tensor weight, torch::Tensor bias, torch::Tensor scale, torch::Tensor sw, torch::Tensor sb) {\n int C = weight.size(0), K = weight.size(1); long total = (long)C*K;\n int threads=256, blocks=(int)((total+threads-1)/threads); if(blocks>4096) blocks=4096;\n AT_DISPATCH_FLOATING_TYPES(weight.scalar_type(), \"scale_weight_bias_cuda\", [&] {\n scale_weight_bias_kernel<scalar_t><<<blocks,threads>>>(weight.data_ptr<scalar_t>(), bias.data_ptr<scalar_t>(), scale.data_ptr<scalar_t>(), sw.data_ptr<scalar_t>(), sb.data_ptr<scalar_t>(), C, K, total);\n });\n}\n\n#define COLS 32\n#define ROW_GROUPS 8\n#define ROWS_PER_CHUNK 256\n#define NORM_ROWS 256\n\ntemplate <typename scalar_t>\n__global__ void partial_sum_kernel(const scalar_t* __restrict__ y, float* __restrict__ ps, float* __restrict__ pss, int N, int C, int chunks) {\n int cb = blockIdx.x * COLS;\n int ch = blockIdx.y;\n int tid = threadIdx.x;\n int col = tid & 31;\n int grp = tid >> 5;\n int c = cb + col;\n int r0 = ch * ROWS_PER_CHUNK;\n int r1 = min(N, r0 + ROWS_PER_CHUNK);\n float sum = 0.0f, ss = 0.0f;\n if (c < C) {\n for (int r = r0 + grp; r < r1; r += ROW_GROUPS) {\n float v = (float)y[(long)r * C + c];\n sum += v; ss += v * v;\n }\n }\n __shared__ float shs[256];\n __shared__ float shq[256];\n shs[tid] = sum; shq[tid] = ss;\n __syncthreads();\n if (tid < COLS && c < C) {\n float s = 0.0f, q = 0.0f;\n #pragma unroll\n for (int g=0; g<ROW_GROUPS; ++g) { s += shs[g*COLS + tid]; q += shq[g*COLS + tid]; }\n ps[(long)ch * C + c] = s;\n pss[(long)ch * C + c] = q;\n }\n}\n\ntemplate <typename scalar_t>\n__global__ void normalize_train_kernel(scalar_t* __restrict__ y, const float* __restrict__ ps, const float* __restrict__ pss,\n const scalar_t* __restrict__ gamma, const scalar_t* __restrict__ beta,\n scalar_t* __restrict__ running_mean, scalar_t* __restrict__ running_var,\n int N, int C, int chunks, float eps, float momentum) {\n int cb = blockIdx.x * COLS;\n int row_tile = blockIdx.y;\n int tid = threadIdx.x;\n int col = tid & 31;\n int grp = tid >> 5;\n int c = cb + col;\n __shared__ float mean_sh[COLS];\n __shared__ float coef_sh[COLS];\n __shared__ float beta_sh[COLS];\n if (tid < COLS && c < C) {\n float s = 0.0f, q = 0.0f;\n for (int ch=0; ch<chunks; ++ch) { s += ps[(long)ch*C + c]; q += pss[(long)ch*C + c]; }\n float mean = s / (float)N;\n float var = q / (float)N - mean * mean;\n if (var < 0.0f) var = 0.0f;\n float inv = rsqrtf(var + eps);\n mean_sh[col] = mean;\n coef_sh[col] = ((float)gamma[c]) * inv;\n beta_sh[col] = (float)beta[c];\n if (row_tile == 0) {\n running_mean[c] = (scalar_t)(((1.0f - momentum) * (float)running_mean[c]) + momentum * mean);\n float unb = var * ((float)N / (float)(N - 1));\n running_var[c] = (scalar_t)(((1.0f - momentum) * (float)running_var[c]) + momentum * unb);\n }\n }\n __syncthreads();\n if (c < C) {\n int r0 = row_tile * NORM_ROWS;\n int r1 = min(N, r0 + NORM_ROWS);\n float mean = mean_sh[col], coef = coef_sh[col], be = beta_sh[col];\n for (int r = r0 + grp; r < r1; r += ROW_GROUPS) {\n long idx = (long)r * C + c;\n float v = (float)y[idx];\n y[idx] = (scalar_t)((v - mean) * coef + be);\n }\n }\n}\n\ntemplate <typename scalar_t>\n__global__ void bn_eval_kernel(scalar_t* __restrict__ y, const scalar_t* __restrict__ gamma, const scalar_t* __restrict__ beta,\n const scalar_t* __restrict__ running_mean, const scalar_t* __restrict__ running_var,\n int N, int C, float eps) {\n long total = (long)N*C;\n long idx = (long)blockIdx.x * blockDim.x + threadIdx.x;\n long stride = (long)gridDim.x * blockDim.x;\n for (long i=idx; i<total; i+=stride) {\n int c = (int)(i % C);\n float v = (float)y[i];\n float out = (v - (float)running_mean[c]) * rsqrtf((float)running_var[c] + eps) * (float)gamma[c] + (float)beta[c];\n y[i] = (scalar_t)out;\n }\n}\n\nvoid bn_train_cuda(torch::Tensor y, torch::Tensor bn_weight, torch::Tensor bn_bias, torch::Tensor running_mean, torch::Tensor running_var, double eps, double momentum) {\n int N = y.size(0), C = y.size(1);\n int chunks = (N + ROWS_PER_CHUNK - 1) / ROWS_PER_CHUNK;\n auto opts = y.options().dtype(torch::kFloat32);\n auto ps = torch::empty({chunks, C}, opts);\n auto pss = torch::empty({chunks, C}, opts);\n dim3 grid1((C + COLS - 1)/COLS, chunks);\n dim3 block(256);\n dim3 grid2((C + COLS - 1)/COLS, (N + NORM_ROWS - 1)/NORM_ROWS);\n AT_DISPATCH_FLOATING_TYPES(y.scalar_type(), \"bn_train_cuda\", [&] {\n partial_sum_kernel<scalar_t><<<grid1, block>>>(y.data_ptr<scalar_t>(), ps.data_ptr<float>(), pss.data_ptr<float>(), N, C, chunks);\n normalize_train_kernel<scalar_t><<<grid2, block>>>(y.data_ptr<scalar_t>(), ps.data_ptr<float>(), pss.data_ptr<float>(), bn_weight.data_ptr<scalar_t>(), bn_bias.data_ptr<scalar_t>(), running_mean.data_ptr<scalar_t>(), running_var.data_ptr<scalar_t>(), N, C, chunks, (float)eps, (float)momentum);\n });\n}\n\nvoid bn_eval_cuda(torch::Tensor y, torch::Tensor bn_weight, torch::Tensor bn_bias, torch::Tensor running_mean, torch::Tensor running_var, double eps) {\n int N=y.size(0), C=y.size(1); long total=(long)N*C; int threads=256; int blocks=(int)((total+threads-1)/threads); if(blocks>4096) blocks=4096;\n AT_DISPATCH_FLOATING_TYPES(y.scalar_type(), \"bn_eval_cuda\", [&] {\n bn_eval_kernel<scalar_t><<<blocks,threads>>>(y.data_ptr<scalar_t>(), bn_weight.data_ptr<scalar_t>(), bn_bias.data_ptr<scalar_t>(), running_mean.data_ptr<scalar_t>(), running_var.data_ptr<scalar_t>(), N, C, (float)eps);\n });\n}\n'''\n\n_ext = load_inline(name='fused_linear_scale_bn_ext_v2', cpp_sources=cpp_source, cuda_sources=cuda_source,\n functions=['forward'], extra_cflags=['-O3'], extra_cuda_cflags=['-O3'], verbose=False)\n\nclass ModelNew(nn.Module):\n def __init__(self, in_features, out_features, scale_shape, eps=1e-5, momentum=0.1):\n super(ModelNew, self).__init__()\n self.gemm = nn.Linear(in_features, out_features)\n self.scale = nn.Parameter(torch.randn(scale_shape))\n self.bn = nn.BatchNorm1d(out_features, eps=eps, momentum=momentum)\n def forward(self, x):\n return _ext.forward(x, self.gemm.weight, self.gemm.bias, self.scale,\n self.bn.weight, self.bn.bias, self.bn.running_mean, self.bn.running_var,\n self.training, float(self.bn.eps), float(self.bn.momentum))\n" }
result ← compile_kernel (ok)
okcompile_kernel compile_kernel PASSED: kernel compiled without errors.
turn 4 · 1 tool call(s) · 15.8s turn · 15.8s LLM · 10,185 chars (click to expand)
turn 415.8s turn · 15.8s LLM · 0.0s tools
call → run_correctness
callrun_correctness { "kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\ncpp_source = r'''\n#include <torch/extension.h>\n\nvoid scale_weight_bias_cuda(torch::Tensor weight, torch::Tensor bias, torch::Tensor scale, torch::Tensor sw, torch::Tensor sb);\nvoid bn_train_cuda(torch::Tensor y, torch::Tensor bn_weight, torch::Tensor bn_bias, torch::Tensor running_mean, torch::Tensor running_var, double eps, double momentum);\nvoid bn_eval_cuda(torch::Tensor y, torch::Tensor bn_weight, torch::Tensor bn_bias, torch::Tensor running_mean, torch::Tensor running_var, double eps);\n\ntorch::Tensor forward(torch::Tensor x, torch::Tensor weight, torch::Tensor bias, torch::Tensor scale,\n torch::Tensor bn_weight, torch::Tensor bn_bias,\n torch::Tensor running_mean, torch::Tensor running_var,\n bool training, double eps, double momentum) {\n at::NoGradGuard no_grad;\n auto sw = torch::empty_like(weight);\n auto sb = torch::empty_like(bias);\n scale_weight_bias_cuda(weight, bias, scale, sw, sb);\n auto y = torch::addmm(sb, x, sw.t());\n if (training) bn_train_cuda(y, bn_weight, bn_bias, running_mean, running_var, eps, momentum);\n else bn_eval_cuda(y, bn_weight, bn_bias, running_mean, running_var, eps);\n return y;\n}\n'''\n\ncuda_source = r'''\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <math.h>\n\ntemplate <typename scalar_t>\n__global__ void scale_weight_bias_kernel(const scalar_t* __restrict__ w, const scalar_t* __restrict__ b,\n const scalar_t* __restrict__ s, scalar_t* __restrict__ sw,\n scalar_t* __restrict__ sb, int C, int K, long total) {\n long idx = blockIdx.x * blockDim.x + threadIdx.x;\n long stride = (long)blockDim.x * gridDim.x;\n for (long i = idx; i < total; i += stride) {\n int o = (int)(i / K);\n sw[i] = w[i] * s[o];\n }\n for (int o = idx; o < C; o += stride) sb[o] = b[o] * s[o];\n}\n\nvoid scale_weight_bias_cuda(torch::Tensor weight, torch::Tensor bias, torch::Tensor scale, torch::Tensor sw, torch::Tensor sb) {\n int C = weight.size(0), K = weight.size(1); long total = (long)C*K;\n int threads=256, blocks=(int)((total+threads-1)/threads); if(blocks>4096) blocks=4096;\n AT_DISPATCH_FLOATING_TYPES(weight.scalar_type(), \"scale_weight_bias_cuda\", [&] {\n scale_weight_bias_kernel<scalar_t><<<blocks,threads>>>(weight.data_ptr<scalar_t>(), bias.data_ptr<scalar_t>(), scale.data_ptr<scalar_t>(), sw.data_ptr<scalar_t>(), sb.data_ptr<scalar_t>(), C, K, total);\n });\n}\n\n#define COLS 32\n#define ROW_GROUPS 8\n#define ROWS_PER_CHUNK 256\n#define NORM_ROWS 256\n\ntemplate <typename scalar_t>\n__global__ void partial_sum_kernel(const scalar_t* __restrict__ y, float* __restrict__ ps, float* __restrict__ pss, int N, int C, int chunks) {\n int cb = blockIdx.x * COLS;\n int ch = blockIdx.y;\n int tid = threadIdx.x;\n int col = tid & 31;\n int grp = tid >> 5;\n int c = cb + col;\n int r0 = ch * ROWS_PER_CHUNK;\n int r1 = min(N, r0 + ROWS_PER_CHUNK);\n float sum = 0.0f, ss = 0.0f;\n if (c < C) {\n for (int r = r0 + grp; r < r1; r += ROW_GROUPS) {\n float v = (float)y[(long)r * C + c];\n sum += v; ss += v * v;\n }\n }\n __shared__ float shs[256];\n __shared__ float shq[256];\n shs[tid] = sum; shq[tid] = ss;\n __syncthreads();\n if (tid < COLS && c < C) {\n float s = 0.0f, q = 0.0f;\n #pragma unroll\n for (int g=0; g<ROW_GROUPS; ++g) { s += shs[g*COLS + tid]; q += shq[g*COLS + tid]; }\n ps[(long)ch * C + c] = s;\n pss[(long)ch * C + c] = q;\n }\n}\n\ntemplate <typename scalar_t>\n__global__ void normalize_train_kernel(scalar_t* __restrict__ y, const float* __restrict__ ps, const float* __restrict__ pss,\n const scalar_t* __restrict__ gamma, const scalar_t* __restrict__ beta,\n scalar_t* __restrict__ running_mean, scalar_t* __restrict__ running_var,\n int N, int C, int chunks, float eps, float momentum) {\n int cb = blockIdx.x * COLS;\n int row_tile = blockIdx.y;\n int tid = threadIdx.x;\n int col = tid & 31;\n int grp = tid >> 5;\n int c = cb + col;\n __shared__ float mean_sh[COLS];\n __shared__ float coef_sh[COLS];\n __shared__ float beta_sh[COLS];\n if (tid < COLS && c < C) {\n float s = 0.0f, q = 0.0f;\n for (int ch=0; ch<chunks; ++ch) { s += ps[(long)ch*C + c]; q += pss[(long)ch*C + c]; }\n float mean = s / (float)N;\n float var = q / (float)N - mean * mean;\n if (var < 0.0f) var = 0.0f;\n float inv = rsqrtf(var + eps);\n mean_sh[col] = mean;\n coef_sh[col] = ((float)gamma[c]) * inv;\n beta_sh[col] = (float)beta[c];\n if (row_tile == 0) {\n running_mean[c] = (scalar_t)(((1.0f - momentum) * (float)running_mean[c]) + momentum * mean);\n float unb = var * ((float)N / (float)(N - 1));\n running_var[c] = (scalar_t)(((1.0f - momentum) * (float)running_var[c]) + momentum * unb);\n }\n }\n __syncthreads();\n if (c < C) {\n int r0 = row_tile * NORM_ROWS;\n int r1 = min(N, r0 + NORM_ROWS);\n float mean = mean_sh[col], coef = coef_sh[col], be = beta_sh[col];\n for (int r = r0 + grp; r < r1; r += ROW_GROUPS) {\n long idx = (long)r * C + c;\n float v = (float)y[idx];\n y[idx] = (scalar_t)((v - mean) * coef + be);\n }\n }\n}\n\ntemplate <typename scalar_t>\n__global__ void bn_eval_kernel(scalar_t* __restrict__ y, const scalar_t* __restrict__ gamma, const scalar_t* __restrict__ beta,\n const scalar_t* __restrict__ running_mean, const scalar_t* __restrict__ running_var,\n int N, int C, float eps) {\n long total = (long)N*C;\n long idx = (long)blockIdx.x * blockDim.x + threadIdx.x;\n long stride = (long)gridDim.x * blockDim.x;\n for (long i=idx; i<total; i+=stride) {\n int c = (int)(i % C);\n float v = (float)y[i];\n float out = (v - (float)running_mean[c]) * rsqrtf((float)running_var[c] + eps) * (float)gamma[c] + (float)beta[c];\n y[i] = (scalar_t)out;\n }\n}\n\nvoid bn_train_cuda(torch::Tensor y, torch::Tensor bn_weight, torch::Tensor bn_bias, torch::Tensor running_mean, torch::Tensor running_var, double eps, double momentum) {\n int N = y.size(0), C = y.size(1);\n int chunks = (N + ROWS_PER_CHUNK - 1) / ROWS_PER_CHUNK;\n auto opts = y.options().dtype(torch::kFloat32);\n auto ps = torch::empty({chunks, C}, opts);\n auto pss = torch::empty({chunks, C}, opts);\n dim3 grid1((C + COLS - 1)/COLS, chunks);\n dim3 block(256);\n dim3 grid2((C + COLS - 1)/COLS, (N + NORM_ROWS - 1)/NORM_ROWS);\n AT_DISPATCH_FLOATING_TYPES(y.scalar_type(), \"bn_train_cuda\", [&] {\n partial_sum_kernel<scalar_t><<<grid1, block>>>(y.data_ptr<scalar_t>(), ps.data_ptr<float>(), pss.data_ptr<float>(), N, C, chunks);\n normalize_train_kernel<scalar_t><<<grid2, block>>>(y.data_ptr<scalar_t>(), ps.data_ptr<float>(), pss.data_ptr<float>(), bn_weight.data_ptr<scalar_t>(), bn_bias.data_ptr<scalar_t>(), running_mean.data_ptr<scalar_t>(), running_var.data_ptr<scalar_t>(), N, C, chunks, (float)eps, (float)momentum);\n });\n}\n\nvoid bn_eval_cuda(torch::Tensor y, torch::Tensor bn_weight, torch::Tensor bn_bias, torch::Tensor running_mean, torch::Tensor running_var, double eps) {\n int N=y.size(0), C=y.size(1); long total=(long)N*C; int threads=256; int blocks=(int)((total+threads-1)/threads); if(blocks>4096) blocks=4096;\n AT_DISPATCH_FLOATING_TYPES(y.scalar_type(), \"bn_eval_cuda\", [&] {\n bn_eval_kernel<scalar_t><<<blocks,threads>>>(y.data_ptr<scalar_t>(), bn_weight.data_ptr<scalar_t>(), bn_bias.data_ptr<scalar_t>(), running_mean.data_ptr<scalar_t>(), running_var.data_ptr<scalar_t>(), N, C, (float)eps);\n });\n}\n'''\n\n_ext = load_inline(name='fused_linear_scale_bn_ext_v2', cpp_sources=cpp_source, cuda_sources=cuda_source,\n functions=['forward'], extra_cflags=['-O3'], extra_cuda_cflags=['-O3'], verbose=False)\n\nclass ModelNew(nn.Module):\n def __init__(self, in_features, out_features, scale_shape, eps=1e-5, momentum=0.1):\n super(ModelNew, self).__init__()\n self.gemm = nn.Linear(in_features, out_features)\n self.scale = nn.Parameter(torch.randn(scale_shape))\n self.bn = nn.BatchNorm1d(out_features, eps=eps, momentum=momentum)\n def forward(self, x):\n return _ext.forward(x, self.gemm.weight, self.gemm.bias, self.scale,\n self.bn.weight, self.bn.bias, self.bn.running_mean, self.bn.running_var,\n self.training, float(self.bn.eps), float(self.bn.momentum))\n" }
result ← run_correctness (ok)
okrun_correctness run_correctness PASSED: (5 / 5) trials all matched the reference. Numerical precision: max_abs_err=7.30e-05 mean_abs_err=2.32e-06 max_rel_err=1.25e+07
turn 5 · 1 tool call(s) · 20.4s turn · 20.4s LLM · 12,036 chars (click to expand)
turn 520.4s turn · 20.4s LLM · 0.0s tools
call → profile_kernel
callprofile_kernel { "kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\ncpp_source = r'''\n#include <torch/extension.h>\n\nvoid scale_weight_bias_cuda(torch::Tensor weight, torch::Tensor bias, torch::Tensor scale, torch::Tensor sw, torch::Tensor sb);\nvoid bn_train_cuda(torch::Tensor y, torch::Tensor bn_weight, torch::Tensor bn_bias, torch::Tensor running_mean, torch::Tensor running_var, double eps, double momentum);\nvoid bn_eval_cuda(torch::Tensor y, torch::Tensor bn_weight, torch::Tensor bn_bias, torch::Tensor running_mean, torch::Tensor running_var, double eps);\n\ntorch::Tensor forward(torch::Tensor x, torch::Tensor weight, torch::Tensor bias, torch::Tensor scale,\n torch::Tensor bn_weight, torch::Tensor bn_bias,\n torch::Tensor running_mean, torch::Tensor running_var,\n bool training, double eps, double momentum) {\n at::NoGradGuard no_grad;\n auto sw = torch::empty_like(weight);\n auto sb = torch::empty_like(bias);\n scale_weight_bias_cuda(weight, bias, scale, sw, sb);\n auto y = torch::addmm(sb, x, sw.t());\n if (training) bn_train_cuda(y, bn_weight, bn_bias, running_mean, running_var, eps, momentum);\n else bn_eval_cuda(y, bn_weight, bn_bias, running_mean, running_var, eps);\n return y;\n}\n'''\n\ncuda_source = r'''\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <math.h>\n\ntemplate <typename scalar_t>\n__global__ void scale_weight_bias_kernel(const scalar_t* __restrict__ w, const scalar_t* __restrict__ b,\n const scalar_t* __restrict__ s, scalar_t* __restrict__ sw,\n scalar_t* __restrict__ sb, int C, int K, long total) {\n long idx = blockIdx.x * blockDim.x + threadIdx.x;\n long stride = (long)blockDim.x * gridDim.x;\n for (long i = idx; i < total; i += stride) {\n int o = (int)(i / K);\n sw[i] = w[i] * s[o];\n }\n for (int o = idx; o < C; o += stride) sb[o] = b[o] * s[o];\n}\n\nvoid scale_weight_bias_cuda(torch::Tensor weight, torch::Tensor bias, torch::Tensor scale, torch::Tensor sw, torch::Tensor sb) {\n int C = weight.size(0), K = weight.size(1); long total = (long)C*K;\n int threads=256, blocks=(int)((total+threads-1)/threads); if(blocks>4096) blocks=4096;\n AT_DISPATCH_FLOATING_TYPES(weight.scalar_type(), \"scale_weight_bias_cuda\", [&] {\n scale_weight_bias_kernel<scalar_t><<<blocks,threads>>>(weight.data_ptr<scalar_t>(), bias.data_ptr<scalar_t>(), scale.data_ptr<scalar_t>(), sw.data_ptr<scalar_t>(), sb.data_ptr<scalar_t>(), C, K, total);\n });\n}\n\n#define COLS 32\n#define ROW_GROUPS 8\n#define ROWS_PER_CHUNK 256\n#define NORM_ROWS 256\n\ntemplate <typename scalar_t>\n__global__ void partial_sum_kernel(const scalar_t* __restrict__ y, float* __restrict__ ps, float* __restrict__ pss, int N, int C, int chunks) {\n int cb = blockIdx.x * COLS;\n int ch = blockIdx.y;\n int tid = threadIdx.x;\n int col = tid & 31;\n int grp = tid >> 5;\n int c = cb + col;\n int r0 = ch * ROWS_PER_CHUNK;\n int r1 = min(N, r0 + ROWS_PER_CHUNK);\n float sum = 0.0f, ss = 0.0f;\n if (c < C) {\n for (int r = r0 + grp; r < r1; r += ROW_GROUPS) {\n float v = (float)y[(long)r * C + c];\n sum += v; ss += v * v;\n }\n }\n __shared__ float shs[256];\n __shared__ float shq[256];\n shs[tid] = sum; shq[tid] = ss;\n __syncthreads();\n if (tid < COLS && c < C) {\n float s = 0.0f, q = 0.0f;\n #pragma unroll\n for (int g=0; g<ROW_GROUPS; ++g) { s += shs[g*COLS + tid]; q += shq[g*COLS + tid]; }\n ps[(long)ch * C + c] = s;\n pss[(long)ch * C + c] = q;\n }\n}\n\ntemplate <typename scalar_t>\n__global__ void normalize_train_kernel(scalar_t* __restrict__ y, const float* __restrict__ ps, const float* __restrict__ pss,\n const scalar_t* __restrict__ gamma, const scalar_t* __restrict__ beta,\n scalar_t* __restrict__ running_mean, scalar_t* __restrict__ running_var,\n int N, int C, int chunks, float eps, float momentum) {\n int cb = blockIdx.x * COLS;\n int row_tile = blockIdx.y;\n int tid = threadIdx.x;\n int col = tid & 31;\n int grp = tid >> 5;\n int c = cb + col;\n __shared__ float mean_sh[COLS];\n __shared__ float coef_sh[COLS];\n __shared__ float beta_sh[COLS];\n if (tid < COLS && c < C) {\n float s = 0.0f, q = 0.0f;\n for (int ch=0; ch<chunks; ++ch) { s += ps[(long)ch*C + c]; q += pss[(long)ch*C + c]; }\n float mean = s / (float)N;\n float var = q / (float)N - mean * mean;\n if (var < 0.0f) var = 0.0f;\n float inv = rsqrtf(var + eps);\n mean_sh[col] = mean;\n coef_sh[col] = ((float)gamma[c]) * inv;\n beta_sh[col] = (float)beta[c];\n if (row_tile == 0) {\n running_mean[c] = (scalar_t)(((1.0f - momentum) * (float)running_mean[c]) + momentum * mean);\n float unb = var * ((float)N / (float)(N - 1));\n running_var[c] = (scalar_t)(((1.0f - momentum) * (float)running_var[c]) + momentum * unb);\n }\n }\n __syncthreads();\n if (c < C) {\n int r0 = row_tile * NORM_ROWS;\n int r1 = min(N, r0 + NORM_ROWS);\n float mean = mean_sh[col], coef = coef_sh[col], be = beta_sh[col];\n for (int r = r0 + grp; r < r1; r += ROW_GROUPS) {\n long idx = (long)r * C + c;\n float v = (float)y[idx];\n y[idx] = (scalar_t)((v - mean) * coef + be);\n }\n }\n}\n\ntemplate <typename scalar_t>\n__global__ void bn_eval_kernel(scalar_t* __restrict__ y, const scalar_t* __restrict__ gamma, const scalar_t* __restrict__ beta,\n const scalar_t* __restrict__ running_mean, const scalar_t* __restrict__ running_var,\n int N, int C, float eps) {\n long total = (long)N*C;\n long idx = (long)blockIdx.x * blockDim.x + threadIdx.x;\n long stride = (long)gridDim.x * blockDim.x;\n for (long i=idx; i<total; i+=stride) {\n int c = (int)(i % C);\n float v = (float)y[i];\n float out = (v - (float)running_mean[c]) * rsqrtf((float)running_var[c] + eps) * (float)gamma[c] + (float)beta[c];\n y[i] = (scalar_t)out;\n }\n}\n\nvoid bn_train_cuda(torch::Tensor y, torch::Tensor bn_weight, torch::Tensor bn_bias, torch::Tensor running_mean, torch::Tensor running_var, double eps, double momentum) {\n int N = y.size(0), C = y.size(1);\n int chunks = (N + ROWS_PER_CHUNK - 1) / ROWS_PER_CHUNK;\n auto opts = y.options().dtype(torch::kFloat32);\n auto ps = torch::empty({chunks, C}, opts);\n auto pss = torch::empty({chunks, C}, opts);\n dim3 grid1((C + COLS - 1)/COLS, chunks);\n dim3 block(256);\n dim3 grid2((C + COLS - 1)/COLS, (N + NORM_ROWS - 1)/NORM_ROWS);\n AT_DISPATCH_FLOATING_TYPES(y.scalar_type(), \"bn_train_cuda\", [&] {\n partial_sum_kernel<scalar_t><<<grid1, block>>>(y.data_ptr<scalar_t>(), ps.data_ptr<float>(), pss.data_ptr<float>(), N, C, chunks);\n normalize_train_kernel<scalar_t><<<grid2, block>>>(y.data_ptr<scalar_t>(), ps.data_ptr<float>(), pss.data_ptr<float>(), bn_weight.data_ptr<scalar_t>(), bn_bias.data_ptr<scalar_t>(), running_mean.data_ptr<scalar_t>(), running_var.data_ptr<scalar_t>(), N, C, chunks, (float)eps, (float)momentum);\n });\n}\n\nvoid bn_eval_cuda(torch::Tensor y, torch::Tensor bn_weight, torch::Tensor bn_bias, torch::Tensor running_mean, torch::Tensor running_var, double eps) {\n int N=y.size(0), C=y.size(1); long total=(long)N*C; int threads=256; int blocks=(int)((total+threads-1)/threads); if(blocks>4096) blocks=4096;\n AT_DISPATCH_FLOATING_TYPES(y.scalar_type(), \"bn_eval_cuda\", [&] {\n bn_eval_kernel<scalar_t><<<blocks,threads>>>(y.data_ptr<scalar_t>(), bn_weight.data_ptr<scalar_t>(), bn_bias.data_ptr<scalar_t>(), running_mean.data_ptr<scalar_t>(), running_var.data_ptr<scalar_t>(), N, C, (float)eps);\n });\n}\n'''\n\n_ext = load_inline(name='fused_linear_scale_bn_ext_v2', cpp_sources=cpp_source, cuda_sources=cuda_source,\n functions=['forward'], extra_cflags=['-O3'], extra_cuda_cflags=['-O3'], verbose=False)\n\nclass ModelNew(nn.Module):\n def __init__(self, in_features, out_features, scale_shape, eps=1e-5, momentum=0.1):\n super(ModelNew, self).__init__()\n self.gemm = nn.Linear(in_features, out_features)\n self.scale = nn.Parameter(torch.randn(scale_shape))\n self.bn = nn.BatchNorm1d(out_features, eps=eps, momentum=momentum)\n def forward(self, x):\n return _ext.forward(x, self.gemm.weight, self.gemm.bias, self.scale,\n self.bn.weight, self.bn.bias, self.bn.running_mean, self.bn.running_var,\n self.training, float(self.bn.eps), float(self.bn.momentum))\n" }
result ← profile_kernel (ok)
okprofile_kernel profile_kernel PASSED: profiling complete. === Kernel Profile Summary === --- Kernel Breakdown (by GPU time) --- #1: aten::addmm 86502.0μs (98%) calls=1 #2: aten::empty_like 624.0μs (1%) calls=1 #3: aten::empty_strided 595.0μs (1%) calls=1 #4: aten::t 50.0μs (0%) calls=1 #5: aten::transpose 37.0μs (0%) calls=1 ... +5 more kernels --- Timing --- GPU time: 11184.48 μs [↑4.0μs REGRESSED] --- Memory Bandwidth --- DRAM BW: 7643.5/3350 GB/s (100.0%) read=5734.5 write=1908.9 GB/s Hit rates: L2=177.4% --- Compute Throughput --- Dominant pipe: FP32 util=105.2% [↓31.3% REGRESSED] Pipe util: FMA=105.2%, Tensor=0.9% --- Roofline Analysis --- Ridge point (FP32): 20.00 FLOPs/byte Classification: COMPUTE-BOUND --- Occupancy --- Achieved: 291.0% [↑18.7% ok] Eligible warps/cycle: 2.71 Launch: regs/thread=344, smem/blk=40320B, block=896, grid=24576 --- Memory Access Quality --- Global ld coalescing: 14.50 sectors/req (14.5x wasted BW) --- Warp Stall Reasons (% of issue cycles) --- long_scoreboard 23306.8% #################### short_scoreboard 169.0% #################### mio_throttle 55.2% ########### --- Optimization Hints --- -> 23307% stalls: global memory latency (waiting for DRAM/L2). + sectors/req=14.5 -> uncoalesced global loads, restructure access pattern or use __ldg Try shared-memory tiling, prefetching, or reducing working set. -> Global load coalescing=14.5x (ideal=1.0) -> 15x wasted DRAM BW. Make per-warp accesses contiguous. -> Compute-bound but NOT using tensor cores. If applicable, use FP16/BF16 with torch.matmul or WMMA intrinsics. --- Delta vs Previous Iteration --- GPU time: 11180.48→11184.48μs (↑4.00) REGRESSED Compute%: 136.50→105.19% (↓31.31) REGRESSED Occupancy: 272.23→290.95% (↑18.72) ok Eligible warps/cyc: 5.10→2.71 (↓2.39) REGRESSED Ld sectors/req: 18.50→14.50 (↓4.00) ok L2 hit%: 386.61→177.41% (↓209.20) REGRESSED
turn 6 · 1 tool call(s) · 30.5s turn · 30.5s LLM · 14,449 chars (click to expand)
turn 630.5s turn · 30.5s LLM · 0.0s tools
reasoning
**Evaluating correctness and efficiency** I'm considering if correctness improves after copying state and making one call. It could really speed things up. Static methods might not reward hacking, but what if legitimate precomputation with constant scaled weights helps? Also, if training weights can update through the optimizer, I need to ensure that ModelNew behaves correctly when params change via load_state. I’ll focus on implementing functions like make_scaled and explore options for returning vectors. It’s interesting to think how Pybind integrates with this!
call → compile_kernel
callcompile_kernel { "kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\ncpp_source = r'''\n#include <torch/extension.h>\n#include <vector>\n\nvoid scale_weight_bias_cuda(torch::Tensor weight, torch::Tensor bias, torch::Tensor scale, torch::Tensor sw, torch::Tensor sb);\nvoid bn_train_cuda(torch::Tensor y, torch::Tensor bn_weight, torch::Tensor bn_bias, torch::Tensor running_mean, torch::Tensor running_var, double eps, double momentum);\nvoid bn_eval_cuda(torch::Tensor y, torch::Tensor bn_weight, torch::Tensor bn_bias, torch::Tensor running_mean, torch::Tensor running_var, double eps);\n\nstd::vector<torch::Tensor> scale_params(torch::Tensor weight, torch::Tensor bias, torch::Tensor scale) {\n at::NoGradGuard no_grad;\n auto sw = torch::empty_like(weight);\n auto sb = torch::empty_like(bias);\n scale_weight_bias_cuda(weight, bias, scale, sw, sb);\n return {sw, sb};\n}\n\ntorch::Tensor forward_cached(torch::Tensor x, torch::Tensor sw, torch::Tensor sb,\n torch::Tensor bn_weight, torch::Tensor bn_bias,\n torch::Tensor running_mean, torch::Tensor running_var,\n bool training, double eps, double momentum) {\n at::NoGradGuard no_grad;\n auto y = torch::addmm(sb, x, sw.t());\n if (training) bn_train_cuda(y, bn_weight, bn_bias, running_mean, running_var, eps, momentum);\n else bn_eval_cuda(y, bn_weight, bn_bias, running_mean, running_var, eps);\n return y;\n}\n'''\n\ncuda_source = r'''\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <math.h>\n\ntemplate <typename scalar_t>\n__global__ void scale_weight_bias_kernel(const scalar_t* __restrict__ w, const scalar_t* __restrict__ b,\n const scalar_t* __restrict__ s, scalar_t* __restrict__ sw,\n scalar_t* __restrict__ sb, int C, int K, long total) {\n long idx = blockIdx.x * blockDim.x + threadIdx.x;\n long stride = (long)blockDim.x * gridDim.x;\n for (long i = idx; i < total; i += stride) {\n int o = (int)(i / K);\n sw[i] = w[i] * s[o];\n }\n for (int o = idx; o < C; o += stride) sb[o] = b[o] * s[o];\n}\n\nvoid scale_weight_bias_cuda(torch::Tensor weight, torch::Tensor bias, torch::Tensor scale, torch::Tensor sw, torch::Tensor sb) {\n int C = weight.size(0), K = weight.size(1); long total = (long)C*K;\n int threads=256, blocks=(int)((total+threads-1)/threads); if(blocks>4096) blocks=4096;\n AT_DISPATCH_FLOATING_TYPES(weight.scalar_type(), \"scale_weight_bias_cuda\", [&] {\n scale_weight_bias_kernel<scalar_t><<<blocks,threads>>>(weight.data_ptr<scalar_t>(), bias.data_ptr<scalar_t>(), scale.data_ptr<scalar_t>(), sw.data_ptr<scalar_t>(), sb.data_ptr<scalar_t>(), C, K, total);\n });\n}\n\n#define COLS 32\n#define ROW_GROUPS 8\n#define ROWS_PER_CHUNK 256\n#define NORM_ROWS 256\n\ntemplate <typename scalar_t>\n__global__ void partial_sum_kernel(const scalar_t* __restrict__ y, float* __restrict__ ps, float* __restrict__ pss, int N, int C, int chunks) {\n int cb = blockIdx.x * COLS;\n int ch = blockIdx.y;\n int tid = threadIdx.x;\n int col = tid & 31;\n int grp = tid >> 5;\n int c = cb + col;\n int r0 = ch * ROWS_PER_CHUNK;\n int r1 = min(N, r0 + ROWS_PER_CHUNK);\n float sum = 0.0f, ss = 0.0f;\n if (c < C) {\n for (int r = r0 + grp; r < r1; r += ROW_GROUPS) {\n float v = (float)y[(long)r * C + c];\n sum += v; ss += v * v;\n }\n }\n __shared__ float shs[256];\n __shared__ float shq[256];\n shs[tid] = sum; shq[tid] = ss;\n __syncthreads();\n if (tid < COLS && c < C) {\n float s = 0.0f, q = 0.0f;\n #pragma unroll\n for (int g=0; g<ROW_GROUPS; ++g) { s += shs[g*COLS + tid]; q += shq[g*COLS + tid]; }\n ps[(long)ch * C + c] = s;\n pss[(long)ch * C + c] = q;\n }\n}\n\ntemplate <typename scalar_t>\n__global__ void normalize_train_kernel(scalar_t* __restrict__ y, const float* __restrict__ ps, const float* __restrict__ pss,\n const scalar_t* __restrict__ gamma, const scalar_t* __restrict__ beta,\n scalar_t* __restrict__ running_mean, scalar_t* __restrict__ running_var,\n int N, int C, int chunks, float eps, float momentum) {\n int cb = blockIdx.x * COLS;\n int row_tile = blockIdx.y;\n int tid = threadIdx.x;\n int col = tid & 31;\n int grp = tid >> 5;\n int c = cb + col;\n __shared__ float mean_sh[COLS];\n __shared__ float coef_sh[COLS];\n __shared__ float beta_sh[COLS];\n if (tid < COLS && c < C) {\n float s = 0.0f, q = 0.0f;\n for (int ch=0; ch<chunks; ++ch) { s += ps[(long)ch*C + c]; q += pss[(long)ch*C + c]; }\n float mean = s / (float)N;\n float var = q / (float)N - mean * mean;\n if (var < 0.0f) var = 0.0f;\n mean_sh[col] = mean;\n coef_sh[col] = ((float)gamma[c]) * rsqrtf(var + eps);\n beta_sh[col] = (float)beta[c];\n if (row_tile == 0) {\n running_mean[c] = (scalar_t)(((1.0f - momentum) * (float)running_mean[c]) + momentum * mean);\n float unb = var * ((float)N / (float)(N - 1));\n running_var[c] = (scalar_t)(((1.0f - momentum) * (float)running_var[c]) + momentum * unb);\n }\n }\n __syncthreads();\n if (c < C) {\n int r0 = row_tile * NORM_ROWS;\n int r1 = min(N, r0 + NORM_ROWS);\n float mean = mean_sh[col], coef = coef_sh[col], be = beta_sh[col];\n for (int r = r0 + grp; r < r1; r += ROW_GROUPS) {\n long idx = (long)r * C + c;\n float v = (float)y[idx];\n y[idx] = (scalar_t)((v - mean) * coef + be);\n }\n }\n}\n\ntemplate <typename scalar_t>\n__global__ void bn_eval_kernel(scalar_t* __restrict__ y, const scalar_t* __restrict__ gamma, const scalar_t* __restrict__ beta,\n const scalar_t* __restrict__ running_mean, const scalar_t* __restrict__ running_var,\n int N, int C, float eps) {\n long total = (long)N*C;\n long idx = (long)blockIdx.x * blockDim.x + threadIdx.x;\n long stride = (long)gridDim.x * blockDim.x;\n for (long i=idx; i<total; i+=stride) {\n int c = (int)(i % C);\n float v = (float)y[i];\n float out = (v - (float)running_mean[c]) * rsqrtf((float)running_var[c] + eps) * (float)gamma[c] + (float)beta[c];\n y[i] = (scalar_t)out;\n }\n}\n\nvoid bn_train_cuda(torch::Tensor y, torch::Tensor bn_weight, torch::Tensor bn_bias, torch::Tensor running_mean, torch::Tensor running_var, double eps, double momentum) {\n int N = y.size(0), C = y.size(1);\n int chunks = (N + ROWS_PER_CHUNK - 1) / ROWS_PER_CHUNK;\n auto opts = y.options().dtype(torch::kFloat32);\n auto ps = torch::empty({chunks, C}, opts);\n auto pss = torch::empty({chunks, C}, opts);\n dim3 grid1((C + COLS - 1)/COLS, chunks);\n dim3 block(256);\n dim3 grid2((C + COLS - 1)/COLS, (N + NORM_ROWS - 1)/NORM_ROWS);\n AT_DISPATCH_FLOATING_TYPES(y.scalar_type(), \"bn_train_cuda\", [&] {\n partial_sum_kernel<scalar_t><<<grid1, block>>>(y.data_ptr<scalar_t>(), ps.data_ptr<float>(), pss.data_ptr<float>(), N, C, chunks);\n normalize_train_kernel<scalar_t><<<grid2, block>>>(y.data_ptr<scalar_t>(), ps.data_ptr<float>(), pss.data_ptr<float>(), bn_weight.data_ptr<scalar_t>(), bn_bias.data_ptr<scalar_t>(), running_mean.data_ptr<scalar_t>(), running_var.data_ptr<scalar_t>(), N, C, chunks, (float)eps, (float)momentum);\n });\n}\n\nvoid bn_eval_cuda(torch::Tensor y, torch::Tensor bn_weight, torch::Tensor bn_bias, torch::Tensor running_mean, torch::Tensor running_var, double eps) {\n int N=y.size(0), C=y.size(1); long total=(long)N*C; int threads=256; int blocks=(int)((total+threads-1)/threads); if(blocks>4096) blocks=4096;\n AT_DISPATCH_FLOATING_TYPES(y.scalar_type(), \"bn_eval_cuda\", [&] {\n bn_eval_kernel<scalar_t><<<blocks,threads>>>(y.data_ptr<scalar_t>(), bn_weight.data_ptr<scalar_t>(), bn_bias.data_ptr<scalar_t>(), running_mean.data_ptr<scalar_t>(), running_var.data_ptr<scalar_t>(), N, C, (float)eps);\n });\n}\n'''\n\n_ext = load_inline(name='fused_linear_scale_bn_ext_v3', cpp_sources=cpp_source, cuda_sources=cuda_source,\n functions=['scale_params','forward_cached'], extra_cflags=['-O3'], extra_cuda_cflags=['-O3'], verbose=False)\n\nclass ModelNew(nn.Module):\n def __init__(self, in_features, out_features, scale_shape, eps=1e-5, momentum=0.1):\n super(ModelNew, self).__init__()\n self.gemm = nn.Linear(in_features, out_features)\n self.scale = nn.Parameter(torch.randn(scale_shape))\n self.bn = nn.BatchNorm1d(out_features, eps=eps, momentum=momentum)\n self._sw = None\n self._sb = None\n self._cache_versions = None\n def _ensure_cache(self):\n vers = (self.gemm.weight._version, self.gemm.bias._version, self.scale._version,\n self.gemm.weight.data_ptr(), self.gemm.bias.data_ptr(), self.scale.data_ptr())\n if self._sw is None or self._cache_versions != vers:\n sw, sb = _ext.scale_params(self.gemm.weight, self.gemm.bias, self.scale)\n self._sw = sw\n self._sb = sb\n self._cache_versions = vers\n def forward(self, x):\n self._ensure_cache()\n return _ext.forward_cached(x, self._sw, self._sb,\n self.bn.weight, self.bn.bias, self.bn.running_mean, self.bn.running_var,\n self.training, float(self.bn.eps), float(self.bn.momentum))\n" }
result ← compile_kernel (fail)
failcompile_kernel compile_kernel FAILED: RuntimeError. RuntimeError: Error building extension 'fused_linear_scale_bn_ext_v3': [1/3] /opt/conda/bin/x86_64-conda-linux-gnu-c++ -MMD -MF main.o.d -DTORCH_EXTENSION_NAME=fused_linear_scale_bn_ext_v3 -DTORCH_API_INCLUDE_EXTENSION_H -isystem /scratch/abaner/KernelBench/.venv/lib/python3.10/site-packages/torch/include -isystem /scratch/abaner/KernelBench/.venv/lib/python3.10/site-packages/torch/include/torch/csrc/api/include -isystem /usr/local/cuda/include -isystem /opt/conda/include/python3.10 -fPIC -std=c++17 -O3 -c /scratch/abaner/KernelBench/runs/l12_all_gpt/original/gpt-5.5-priority/level_2_problem_39_cache/k_340a6841c105/fused_linear_scale_bn_ext_v3/main.cpp -o main.o [2/3] /usr/local/cuda/bin/nvcc -MD -MF cuda.cuda.o.d -ccbin /opt/conda/bin/x86_64-conda-linux-gnu-cc -DTORCH_EXTENSION_NAME=fused_linear_scale_bn_ext_v3 -DTORCH_API_INCLUDE_EXTENSION_H -isystem /scratch/abaner/KernelBench/.venv/lib/python3.10/site-packages/torch/include -isystem /scratch/abaner/KernelBench/.venv/lib/python3.10/site-packages/torch/include/torch/csrc/api/include -isystem /usr/local/cuda/include -isystem /opt/conda/include/python3.10 -D__CUDA_NO_HALF_OPERATORS__ -D__CUDA_NO_HALF_CONVERSIONS__ -D__CUDA_NO_BFLOAT16_CONVERSIONS__ -D__CUDA_NO_HALF2_OPERATORS__ --expt-relaxed-constexpr -gencode=arch=compute_90,code=compute_90 -gencode=arch=compute_90,code=sm_90 --compiler-options '-fPIC' -O3 -std=c++17 -c /scratch/abaner/KernelBench/runs/l12_all_gpt/original/gpt-5.5-priority/level_2_problem_39_cache/k_340a6841c105/fused_linear_scale_bn_ext_v3/cuda.cu -o cuda.cuda.o FAILED: [code=1] cuda.cuda.o /usr/local/cuda/bin/nvcc -MD -MF cuda.cuda.o.d -ccbin /opt/conda/bin/x86_64-conda-linux-gnu-cc -DTORCH_EXTENSION_NAME=fused_linear_scale_bn_ext_v3 -DTORCH_API_INCLUDE_EXTENSION_H -isystem /scratch/abaner/KernelBench/.venv/lib/python3.10/site-packages/torch/include -isystem /scratch/abaner/KernelBench/.venv/lib/python3.10/site-packages/torch/include/torch/csrc/api/include -isystem /usr/local/cuda/include -isystem /opt/conda/include/python3.10 -D__CUDA_NO_HALF_OPERATORS__ -D__CUDA_NO_HALF_CONVERSIONS__ -D__CUDA_NO_BFLOAT16_CONVERSIONS__ -D__CUDA_NO_HALF2_OPERATORS__ --expt-relaxed-constexpr -gencode=arch=compute_90,code=compute_90 -gencode=arch=compute_90,code=sm_90 --compiler-options '-fPIC' -O3 -std=c++17 -c /scratch/abaner/KernelBench/runs/l12_all_gpt/original/gpt-5.5-priority/level_2_problem_39_cache/k_340a6841c105/fused_linear_scale_bn_ext_v3/cuda.cu -o cuda.cuda.o nvcc warning : incompatible redefinition for option 'compiler-bindir', the last value of this option was used In file included from tmpxft_0035c295_00000000-6_cuda.cudafe1.stub.c:1: /var/tmp/tmpxft_0035c295_00000000-6_cuda.cudafe1.stub.c:81:27: fatal error: error writing to /var/tmp/cc9YGkWg.s: No space left on device 81 | #pragma GCC diagnostic pop | ^ compilation terminated. ninja: build stopped: subcommand failed.
turn 7 · 1 tool call(s) · 28.0s turn · 28.0s LLM · 10,077 chars (click to expand)
turn 728.0s turn · 28.0s LLM · 0.0s tools
call → static_check
callstatic_check { "kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\ncpp_source = r'''\n#include <torch/extension.h>\n\nvoid scale_weight_bias_cuda(torch::Tensor weight, torch::Tensor bias, torch::Tensor scale, torch::Tensor sw, torch::Tensor sb);\nvoid bn_train_cuda(torch::Tensor y, torch::Tensor bn_weight, torch::Tensor bn_bias, torch::Tensor running_mean, torch::Tensor running_var, double eps, double momentum);\nvoid bn_eval_cuda(torch::Tensor y, torch::Tensor bn_weight, torch::Tensor bn_bias, torch::Tensor running_mean, torch::Tensor running_var, double eps);\n\ntorch::Tensor forward(torch::Tensor x, torch::Tensor weight, torch::Tensor bias, torch::Tensor scale,\n torch::Tensor bn_weight, torch::Tensor bn_bias,\n torch::Tensor running_mean, torch::Tensor running_var,\n bool training, double eps, double momentum) {\n at::NoGradGuard no_grad;\n auto sw = torch::empty_like(weight);\n auto sb = torch::empty_like(bias);\n scale_weight_bias_cuda(weight, bias, scale, sw, sb);\n auto y = torch::addmm(sb, x, sw.t());\n if (training) bn_train_cuda(y, bn_weight, bn_bias, running_mean, running_var, eps, momentum);\n else bn_eval_cuda(y, bn_weight, bn_bias, running_mean, running_var, eps);\n return y;\n}\n'''\n\ncuda_source = r'''\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <math.h>\n\ntemplate <typename scalar_t>\n__global__ void scale_weight_bias_kernel(const scalar_t* __restrict__ w, const scalar_t* __restrict__ b,\n const scalar_t* __restrict__ s, scalar_t* __restrict__ sw,\n scalar_t* __restrict__ sb, int C, int K, long total) {\n long idx = blockIdx.x * blockDim.x + threadIdx.x;\n long stride = (long)blockDim.x * gridDim.x;\n for (long i = idx; i < total; i += stride) {\n int o = (int)(i / K);\n sw[i] = w[i] * s[o];\n }\n for (int o = idx; o < C; o += stride) sb[o] = b[o] * s[o];\n}\n\nvoid scale_weight_bias_cuda(torch::Tensor weight, torch::Tensor bias, torch::Tensor scale, torch::Tensor sw, torch::Tensor sb) {\n int C = weight.size(0), K = weight.size(1); long total = (long)C*K;\n int threads=256, blocks=(int)((total+threads-1)/threads); if(blocks>4096) blocks=4096;\n AT_DISPATCH_FLOATING_TYPES(weight.scalar_type(), \"scale_weight_bias_cuda\", [&] {\n scale_weight_bias_kernel<scalar_t><<<blocks,threads>>>(weight.data_ptr<scalar_t>(), bias.data_ptr<scalar_t>(), scale.data_ptr<scalar_t>(), sw.data_ptr<scalar_t>(), sb.data_ptr<scalar_t>(), C, K, total);\n });\n}\n\n#define COLS 32\n#define ROW_GROUPS 8\n#define ROWS_PER_CHUNK 256\n#define NORM_ROWS 256\n\ntemplate <typename scalar_t>\n__global__ void partial_sum_kernel(const scalar_t* __restrict__ y, float* __restrict__ ps, float* __restrict__ pss, int N, int C, int chunks) {\n int cb = blockIdx.x * COLS;\n int ch = blockIdx.y;\n int tid = threadIdx.x;\n int col = tid & 31;\n int grp = tid >> 5;\n int c = cb + col;\n int r0 = ch * ROWS_PER_CHUNK;\n int r1 = min(N, r0 + ROWS_PER_CHUNK);\n float sum = 0.0f, ss = 0.0f;\n if (c < C) {\n for (int r = r0 + grp; r < r1; r += ROW_GROUPS) {\n float v = (float)y[(long)r * C + c];\n sum += v; ss += v * v;\n }\n }\n __shared__ float shs[256];\n __shared__ float shq[256];\n shs[tid] = sum; shq[tid] = ss;\n __syncthreads();\n if (tid < COLS && c < C) {\n float s = 0.0f, q = 0.0f;\n #pragma unroll\n for (int g=0; g<ROW_GROUPS; ++g) { s += shs[g*COLS + tid]; q += shq[g*COLS + tid]; }\n ps[(long)ch * C + c] = s;\n pss[(long)ch * C + c] = q;\n }\n}\n\ntemplate <typename scalar_t>\n__global__ void normalize_train_kernel(scalar_t* __restrict__ y, const float* __restrict__ ps, const float* __restrict__ pss,\n const scalar_t* __restrict__ gamma, const scalar_t* __restrict__ beta,\n scalar_t* __restrict__ running_mean, scalar_t* __restrict__ running_var,\n int N, int C, int chunks, float eps, float momentum) {\n int cb = blockIdx.x * COLS;\n int row_tile = blockIdx.y;\n int tid = threadIdx.x;\n int col = tid & 31;\n int grp = tid >> 5;\n int c = cb + col;\n __shared__ float mean_sh[COLS];\n __shared__ float coef_sh[COLS];\n __shared__ float beta_sh[COLS];\n if (tid < COLS && c < C) {\n float s = 0.0f, q = 0.0f;\n for (int ch=0; ch<chunks; ++ch) { s += ps[(long)ch*C + c]; q += pss[(long)ch*C + c]; }\n float mean = s / (float)N;\n float var = q / (float)N - mean * mean;\n if (var < 0.0f) var = 0.0f;\n float inv = rsqrtf(var + eps);\n mean_sh[col] = mean;\n coef_sh[col] = ((float)gamma[c]) * inv;\n beta_sh[col] = (float)beta[c];\n if (row_tile == 0) {\n running_mean[c] = (scalar_t)(((1.0f - momentum) * (float)running_mean[c]) + momentum * mean);\n float unb = var * ((float)N / (float)(N - 1));\n running_var[c] = (scalar_t)(((1.0f - momentum) * (float)running_var[c]) + momentum * unb);\n }\n }\n __syncthreads();\n if (c < C) {\n int r0 = row_tile * NORM_ROWS;\n int r1 = min(N, r0 + NORM_ROWS);\n float mean = mean_sh[col], coef = coef_sh[col], be = beta_sh[col];\n for (int r = r0 + grp; r < r1; r += ROW_GROUPS) {\n long idx = (long)r * C + c;\n float v = (float)y[idx];\n y[idx] = (scalar_t)((v - mean) * coef + be);\n }\n }\n}\n\ntemplate <typename scalar_t>\n__global__ void bn_eval_kernel(scalar_t* __restrict__ y, const scalar_t* __restrict__ gamma, const scalar_t* __restrict__ beta,\n const scalar_t* __restrict__ running_mean, const scalar_t* __restrict__ running_var,\n int N, int C, float eps) {\n long total = (long)N*C;\n long idx = (long)blockIdx.x * blockDim.x + threadIdx.x;\n long stride = (long)gridDim.x * blockDim.x;\n for (long i=idx; i<total; i+=stride) {\n int c = (int)(i % C);\n float v = (float)y[i];\n float out = (v - (float)running_mean[c]) * rsqrtf((float)running_var[c] + eps) * (float)gamma[c] + (float)beta[c];\n y[i] = (scalar_t)out;\n }\n}\n\nvoid bn_train_cuda(torch::Tensor y, torch::Tensor bn_weight, torch::Tensor bn_bias, torch::Tensor running_mean, torch::Tensor running_var, double eps, double momentum) {\n int N = y.size(0), C = y.size(1);\n int chunks = (N + ROWS_PER_CHUNK - 1) / ROWS_PER_CHUNK;\n auto opts = y.options().dtype(torch::kFloat32);\n auto ps = torch::empty({chunks, C}, opts);\n auto pss = torch::empty({chunks, C}, opts);\n dim3 grid1((C + COLS - 1)/COLS, chunks);\n dim3 block(256);\n dim3 grid2((C + COLS - 1)/COLS, (N + NORM_ROWS - 1)/NORM_ROWS);\n AT_DISPATCH_FLOATING_TYPES(y.scalar_type(), \"bn_train_cuda\", [&] {\n partial_sum_kernel<scalar_t><<<grid1, block>>>(y.data_ptr<scalar_t>(), ps.data_ptr<float>(), pss.data_ptr<float>(), N, C, chunks);\n normalize_train_kernel<scalar_t><<<grid2, block>>>(y.data_ptr<scalar_t>(), ps.data_ptr<float>(), pss.data_ptr<float>(), bn_weight.data_ptr<scalar_t>(), bn_bias.data_ptr<scalar_t>(), running_mean.data_ptr<scalar_t>(), running_var.data_ptr<scalar_t>(), N, C, chunks, (float)eps, (float)momentum);\n });\n}\n\nvoid bn_eval_cuda(torch::Tensor y, torch::Tensor bn_weight, torch::Tensor bn_bias, torch::Tensor running_mean, torch::Tensor running_var, double eps) {\n int N=y.size(0), C=y.size(1); long total=(long)N*C; int threads=256; int blocks=(int)((total+threads-1)/threads); if(blocks>4096) blocks=4096;\n AT_DISPATCH_FLOATING_TYPES(y.scalar_type(), \"bn_eval_cuda\", [&] {\n bn_eval_kernel<scalar_t><<<blocks,threads>>>(y.data_ptr<scalar_t>(), bn_weight.data_ptr<scalar_t>(), bn_bias.data_ptr<scalar_t>(), running_mean.data_ptr<scalar_t>(), running_var.data_ptr<scalar_t>(), N, C, (float)eps);\n });\n}\n'''\n\n_ext = load_inline(name='fused_linear_scale_bn_ext_v2', cpp_sources=cpp_source, cuda_sources=cuda_source,\n functions=['forward'], extra_cflags=['-O3'], extra_cuda_cflags=['-O3'], verbose=False)\n\nclass ModelNew(nn.Module):\n def __init__(self, in_features, out_features, scale_shape, eps=1e-5, momentum=0.1):\n super(ModelNew, self).__init__()\n self.gemm = nn.Linear(in_features, out_features)\n self.scale = nn.Parameter(torch.randn(scale_shape))\n self.bn = nn.BatchNorm1d(out_features, eps=eps, momentum=momentum)\n def forward(self, x):\n return _ext.forward(x, self.gemm.weight, self.gemm.bias, self.scale,\n self.bn.weight, self.bn.bias, self.bn.running_mean, self.bn.running_var,\n self.training, float(self.bn.eps), float(self.bn.momentum))\n" }
result ← static_check (ok)
okstatic_check static_check PASSED: no violations or warnings detected.
turn 8 · FINAL · 1 tool call(s) · 22.3s turn · 22.3s LLM · 11,677 chars (click to expand)
turn 822.3s turn · 22.3s LLM · 0.0s tools · FINAL
reasoning
**Considering submission options** I’m contemplating whether to submit version 2 or try compiling version 3 while using the same name. I wonder if the static submit speed will be affected. I need to know what the submit returns since it seems we can only use it once for a final version. I’m leaning towards v3 for speed but realize we need a correctness test. There seems to be a space issue causing compilation failure, likely linked to the evaluation process. So, I'm reflecting on whether we can delete the extension cache through Python code. **Evaluating cleanup options** I’m considering including a module-level cleanup before loading inline, like removing old build directories in /scratch. However, I wonder if executing the final code in submit could also help clean space. Using `shutil.rmtree` might raise flags, and without shell access, can I even call it on the torch extensions cache? It feels risky and may not be ideal for final evaluation. If v3 were to include this cleanup, would it address the disk space issue, or is the problem with /var/tmp instead? I think I’ll stick with using version 2 for the final submission.
call → submit_kernel
callsubmit_kernel { "kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\ncpp_source = r'''\n#include <torch/extension.h>\n\nvoid scale_weight_bias_cuda(torch::Tensor weight, torch::Tensor bias, torch::Tensor scale, torch::Tensor sw, torch::Tensor sb);\nvoid bn_train_cuda(torch::Tensor y, torch::Tensor bn_weight, torch::Tensor bn_bias, torch::Tensor running_mean, torch::Tensor running_var, double eps, double momentum);\nvoid bn_eval_cuda(torch::Tensor y, torch::Tensor bn_weight, torch::Tensor bn_bias, torch::Tensor running_mean, torch::Tensor running_var, double eps);\n\ntorch::Tensor forward(torch::Tensor x, torch::Tensor weight, torch::Tensor bias, torch::Tensor scale,\n torch::Tensor bn_weight, torch::Tensor bn_bias,\n torch::Tensor running_mean, torch::Tensor running_var,\n bool training, double eps, double momentum) {\n at::NoGradGuard no_grad;\n auto sw = torch::empty_like(weight);\n auto sb = torch::empty_like(bias);\n scale_weight_bias_cuda(weight, bias, scale, sw, sb);\n auto y = torch::addmm(sb, x, sw.t());\n if (training) bn_train_cuda(y, bn_weight, bn_bias, running_mean, running_var, eps, momentum);\n else bn_eval_cuda(y, bn_weight, bn_bias, running_mean, running_var, eps);\n return y;\n}\n'''\n\ncuda_source = r'''\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <math.h>\n\ntemplate <typename scalar_t>\n__global__ void scale_weight_bias_kernel(const scalar_t* __restrict__ w, const scalar_t* __restrict__ b,\n const scalar_t* __restrict__ s, scalar_t* __restrict__ sw,\n scalar_t* __restrict__ sb, int C, int K, long total) {\n long idx = blockIdx.x * blockDim.x + threadIdx.x;\n long stride = (long)blockDim.x * gridDim.x;\n for (long i = idx; i < total; i += stride) {\n int o = (int)(i / K);\n sw[i] = w[i] * s[o];\n }\n for (int o = idx; o < C; o += stride) sb[o] = b[o] * s[o];\n}\n\nvoid scale_weight_bias_cuda(torch::Tensor weight, torch::Tensor bias, torch::Tensor scale, torch::Tensor sw, torch::Tensor sb) {\n int C = weight.size(0), K = weight.size(1); long total = (long)C*K;\n int threads=256, blocks=(int)((total+threads-1)/threads); if(blocks>4096) blocks=4096;\n AT_DISPATCH_FLOATING_TYPES(weight.scalar_type(), \"scale_weight_bias_cuda\", [&] {\n scale_weight_bias_kernel<scalar_t><<<blocks,threads>>>(weight.data_ptr<scalar_t>(), bias.data_ptr<scalar_t>(), scale.data_ptr<scalar_t>(), sw.data_ptr<scalar_t>(), sb.data_ptr<scalar_t>(), C, K, total);\n });\n}\n\n#define COLS 32\n#define ROW_GROUPS 8\n#define ROWS_PER_CHUNK 256\n#define NORM_ROWS 256\n\ntemplate <typename scalar_t>\n__global__ void partial_sum_kernel(const scalar_t* __restrict__ y, float* __restrict__ ps, float* __restrict__ pss, int N, int C, int chunks) {\n int cb = blockIdx.x * COLS;\n int ch = blockIdx.y;\n int tid = threadIdx.x;\n int col = tid & 31;\n int grp = tid >> 5;\n int c = cb + col;\n int r0 = ch * ROWS_PER_CHUNK;\n int r1 = min(N, r0 + ROWS_PER_CHUNK);\n float sum = 0.0f, ss = 0.0f;\n if (c < C) {\n for (int r = r0 + grp; r < r1; r += ROW_GROUPS) {\n float v = (float)y[(long)r * C + c];\n sum += v; ss += v * v;\n }\n }\n __shared__ float shs[256];\n __shared__ float shq[256];\n shs[tid] = sum; shq[tid] = ss;\n __syncthreads();\n if (tid < COLS && c < C) {\n float s = 0.0f, q = 0.0f;\n #pragma unroll\n for (int g=0; g<ROW_GROUPS; ++g) { s += shs[g*COLS + tid]; q += shq[g*COLS + tid]; }\n ps[(long)ch * C + c] = s;\n pss[(long)ch * C + c] = q;\n }\n}\n\ntemplate <typename scalar_t>\n__global__ void normalize_train_kernel(scalar_t* __restrict__ y, const float* __restrict__ ps, const float* __restrict__ pss,\n const scalar_t* __restrict__ gamma, const scalar_t* __restrict__ beta,\n scalar_t* __restrict__ running_mean, scalar_t* __restrict__ running_var,\n int N, int C, int chunks, float eps, float momentum) {\n int cb = blockIdx.x * COLS;\n int row_tile = blockIdx.y;\n int tid = threadIdx.x;\n int col = tid & 31;\n int grp = tid >> 5;\n int c = cb + col;\n __shared__ float mean_sh[COLS];\n __shared__ float coef_sh[COLS];\n __shared__ float beta_sh[COLS];\n if (tid < COLS && c < C) {\n float s = 0.0f, q = 0.0f;\n for (int ch=0; ch<chunks; ++ch) { s += ps[(long)ch*C + c]; q += pss[(long)ch*C + c]; }\n float mean = s / (float)N;\n float var = q / (float)N - mean * mean;\n if (var < 0.0f) var = 0.0f;\n float inv = rsqrtf(var + eps);\n mean_sh[col] = mean;\n coef_sh[col] = ((float)gamma[c]) * inv;\n beta_sh[col] = (float)beta[c];\n if (row_tile == 0) {\n running_mean[c] = (scalar_t)(((1.0f - momentum) * (float)running_mean[c]) + momentum * mean);\n float unb = var * ((float)N / (float)(N - 1));\n running_var[c] = (scalar_t)(((1.0f - momentum) * (float)running_var[c]) + momentum * unb);\n }\n }\n __syncthreads();\n if (c < C) {\n int r0 = row_tile * NORM_ROWS;\n int r1 = min(N, r0 + NORM_ROWS);\n float mean = mean_sh[col], coef = coef_sh[col], be = beta_sh[col];\n for (int r = r0 + grp; r < r1; r += ROW_GROUPS) {\n long idx = (long)r * C + c;\n float v = (float)y[idx];\n y[idx] = (scalar_t)((v - mean) * coef + be);\n }\n }\n}\n\ntemplate <typename scalar_t>\n__global__ void bn_eval_kernel(scalar_t* __restrict__ y, const scalar_t* __restrict__ gamma, const scalar_t* __restrict__ beta,\n const scalar_t* __restrict__ running_mean, const scalar_t* __restrict__ running_var,\n int N, int C, float eps) {\n long total = (long)N*C;\n long idx = (long)blockIdx.x * blockDim.x + threadIdx.x;\n long stride = (long)gridDim.x * blockDim.x;\n for (long i=idx; i<total; i+=stride) {\n int c = (int)(i % C);\n float v = (float)y[i];\n float out = (v - (float)running_mean[c]) * rsqrtf((float)running_var[c] + eps) * (float)gamma[c] + (float)beta[c];\n y[i] = (scalar_t)out;\n }\n}\n\nvoid bn_train_cuda(torch::Tensor y, torch::Tensor bn_weight, torch::Tensor bn_bias, torch::Tensor running_mean, torch::Tensor running_var, double eps, double momentum) {\n int N = y.size(0), C = y.size(1);\n int chunks = (N + ROWS_PER_CHUNK - 1) / ROWS_PER_CHUNK;\n auto opts = y.options().dtype(torch::kFloat32);\n auto ps = torch::empty({chunks, C}, opts);\n auto pss = torch::empty({chunks, C}, opts);\n dim3 grid1((C + COLS - 1)/COLS, chunks);\n dim3 block(256);\n dim3 grid2((C + COLS - 1)/COLS, (N + NORM_ROWS - 1)/NORM_ROWS);\n AT_DISPATCH_FLOATING_TYPES(y.scalar_type(), \"bn_train_cuda\", [&] {\n partial_sum_kernel<scalar_t><<<grid1, block>>>(y.data_ptr<scalar_t>(), ps.data_ptr<float>(), pss.data_ptr<float>(), N, C, chunks);\n normalize_train_kernel<scalar_t><<<grid2, block>>>(y.data_ptr<scalar_t>(), ps.data_ptr<float>(), pss.data_ptr<float>(), bn_weight.data_ptr<scalar_t>(), bn_bias.data_ptr<scalar_t>(), running_mean.data_ptr<scalar_t>(), running_var.data_ptr<scalar_t>(), N, C, chunks, (float)eps, (float)momentum);\n });\n}\n\nvoid bn_eval_cuda(torch::Tensor y, torch::Tensor bn_weight, torch::Tensor bn_bias, torch::Tensor running_mean, torch::Tensor running_var, double eps) {\n int N=y.size(0), C=y.size(1); long total=(long)N*C; int threads=256; int blocks=(int)((total+threads-1)/threads); if(blocks>4096) blocks=4096;\n AT_DISPATCH_FLOATING_TYPES(y.scalar_type(), \"bn_eval_cuda\", [&] {\n bn_eval_kernel<scalar_t><<<blocks,threads>>>(y.data_ptr<scalar_t>(), bn_weight.data_ptr<scalar_t>(), bn_bias.data_ptr<scalar_t>(), running_mean.data_ptr<scalar_t>(), running_var.data_ptr<scalar_t>(), N, C, (float)eps);\n });\n}\n'''\n\n_ext = load_inline(name='fused_linear_scale_bn_ext_v2', cpp_sources=cpp_source, cuda_sources=cuda_source,\n functions=['forward'], extra_cflags=['-O3'], extra_cuda_cflags=['-O3'], verbose=False)\n\nclass ModelNew(nn.Module):\n def __init__(self, in_features, out_features, scale_shape, eps=1e-5, momentum=0.1):\n super(ModelNew, self).__init__()\n self.gemm = nn.Linear(in_features, out_features)\n self.scale = nn.Parameter(torch.randn(scale_shape))\n self.bn = nn.BatchNorm1d(out_features, eps=eps, momentum=momentum)\n def forward(self, x):\n return _ext.forward(x, self.gemm.weight, self.gemm.bias, self.scale,\n self.bn.weight, self.bn.bias, self.bn.running_mean, self.bn.running_var,\n self.training, float(self.bn.eps), float(self.bn.momentum))\n" }
result ← submit_kernel (ok)
oksubmit_kernel submit_kernel PASSED: (5 / 5) correctness trials all passed. Kernel runtime: 11.30 μs Runtime stats: mean=11.30μs median=?μs std=0.20μs Numerical precision: max_abs_err=7.30e-05 mean_abs_err=2.32e-06 max_rel_err=1.25e+07 Memory: 739.2 MB peak (ref: 933.22 MB, ratio: 0.7921x) Kernel launches: 10 (ref: 20, fusion ratio: 2.0) Energy: 1450.62 mJ/run (ref: 1209.68 mJ, ratio: 0.8339x) SOL score: 1.0
final submitted kernel (183 lines, 8,862 chars) — full source, untruncated
import torch
import torch.nn as nn
from torch.utils.cpp_extension import load_inline

cpp_source = r'''
#include <torch/extension.h>

void scale_weight_bias_cuda(torch::Tensor weight, torch::Tensor bias, torch::Tensor scale, torch::Tensor sw, torch::Tensor sb);
void bn_train_cuda(torch::Tensor y, torch::Tensor bn_weight, torch::Tensor bn_bias, torch::Tensor running_mean, torch::Tensor running_var, double eps, double momentum);
void bn_eval_cuda(torch::Tensor y, torch::Tensor bn_weight, torch::Tensor bn_bias, torch::Tensor running_mean, torch::Tensor running_var, double eps);

torch::Tensor forward(torch::Tensor x, torch::Tensor weight, torch::Tensor bias, torch::Tensor scale,
                      torch::Tensor bn_weight, torch::Tensor bn_bias,
                      torch::Tensor running_mean, torch::Tensor running_var,
                      bool training, double eps, double momentum) {
    at::NoGradGuard no_grad;
    auto sw = torch::empty_like(weight);
    auto sb = torch::empty_like(bias);
    scale_weight_bias_cuda(weight, bias, scale, sw, sb);
    auto y = torch::addmm(sb, x, sw.t());
    if (training) bn_train_cuda(y, bn_weight, bn_bias, running_mean, running_var, eps, momentum);
    else bn_eval_cuda(y, bn_weight, bn_bias, running_mean, running_var, eps);
    return y;
}
'''

cuda_source = r'''
#include <torch/extension.h>
#include <cuda.h>
#include <cuda_runtime.h>
#include <math.h>

template <typename scalar_t>
__global__ void scale_weight_bias_kernel(const scalar_t* __restrict__ w, const scalar_t* __restrict__ b,
                                         const scalar_t* __restrict__ s, scalar_t* __restrict__ sw,
                                         scalar_t* __restrict__ sb, int C, int K, long total) {
    long idx = blockIdx.x * blockDim.x + threadIdx.x;
    long stride = (long)blockDim.x * gridDim.x;
    for (long i = idx; i < total; i += stride) {
        int o = (int)(i / K);
        sw[i] = w[i] * s[o];
    }
    for (int o = idx; o < C; o += stride) sb[o] = b[o] * s[o];
}

void scale_weight_bias_cuda(torch::Tensor weight, torch::Tensor bias, torch::Tensor scale, torch::Tensor sw, torch::Tensor sb) {
    int C = weight.size(0), K = weight.size(1); long total = (long)C*K;
    int threads=256, blocks=(int)((total+threads-1)/threads); if(blocks>4096) blocks=4096;
    AT_DISPATCH_FLOATING_TYPES(weight.scalar_type(), "scale_weight_bias_cuda", [&] {
        scale_weight_bias_kernel<scalar_t><<<blocks,threads>>>(weight.data_ptr<scalar_t>(), bias.data_ptr<scalar_t>(), scale.data_ptr<scalar_t>(), sw.data_ptr<scalar_t>(), sb.data_ptr<scalar_t>(), C, K, total);
    });
}

#define COLS 32
#define ROW_GROUPS 8
#define ROWS_PER_CHUNK 256
#define NORM_ROWS 256

template <typename scalar_t>
__global__ void partial_sum_kernel(const scalar_t* __restrict__ y, float* __restrict__ ps, float* __restrict__ pss, int N, int C, int chunks) {
    int cb = blockIdx.x * COLS;
    int ch = blockIdx.y;
    int tid = threadIdx.x;
    int col = tid & 31;
    int grp = tid >> 5;
    int c = cb + col;
    int r0 = ch * ROWS_PER_CHUNK;
    int r1 = min(N, r0 + ROWS_PER_CHUNK);
    float sum = 0.0f, ss = 0.0f;
    if (c < C) {
        for (int r = r0 + grp; r < r1; r += ROW_GROUPS) {
            float v = (float)y[(long)r * C + c];
            sum += v; ss += v * v;
        }
    }
    __shared__ float shs[256];
    __shared__ float shq[256];
    shs[tid] = sum; shq[tid] = ss;
    __syncthreads();
    if (tid < COLS && c < C) {
        float s = 0.0f, q = 0.0f;
        #pragma unroll
        for (int g=0; g<ROW_GROUPS; ++g) { s += shs[g*COLS + tid]; q += shq[g*COLS + tid]; }
        ps[(long)ch * C + c] = s;
        pss[(long)ch * C + c] = q;
    }
}

template <typename scalar_t>
__global__ void normalize_train_kernel(scalar_t* __restrict__ y, const float* __restrict__ ps, const float* __restrict__ pss,
                                       const scalar_t* __restrict__ gamma, const scalar_t* __restrict__ beta,
                                       scalar_t* __restrict__ running_mean, scalar_t* __restrict__ running_var,
                                       int N, int C, int chunks, float eps, float momentum) {
    int cb = blockIdx.x * COLS;
    int row_tile = blockIdx.y;
    int tid = threadIdx.x;
    int col = tid & 31;
    int grp = tid >> 5;
    int c = cb + col;
    __shared__ float mean_sh[COLS];
    __shared__ float coef_sh[COLS];
    __shared__ float beta_sh[COLS];
    if (tid < COLS && c < C) {
        float s = 0.0f, q = 0.0f;
        for (int ch=0; ch<chunks; ++ch) { s += ps[(long)ch*C + c]; q += pss[(long)ch*C + c]; }
        float mean = s / (float)N;
        float var = q / (float)N - mean * mean;
        if (var < 0.0f) var = 0.0f;
        float inv = rsqrtf(var + eps);
        mean_sh[col] = mean;
        coef_sh[col] = ((float)gamma[c]) * inv;
        beta_sh[col] = (float)beta[c];
        if (row_tile == 0) {
            running_mean[c] = (scalar_t)(((1.0f - momentum) * (float)running_mean[c]) + momentum * mean);
            float unb = var * ((float)N / (float)(N - 1));
            running_var[c] = (scalar_t)(((1.0f - momentum) * (float)running_var[c]) + momentum * unb);
        }
    }
    __syncthreads();
    if (c < C) {
        int r0 = row_tile * NORM_ROWS;
        int r1 = min(N, r0 + NORM_ROWS);
        float mean = mean_sh[col], coef = coef_sh[col], be = beta_sh[col];
        for (int r = r0 + grp; r < r1; r += ROW_GROUPS) {
            long idx = (long)r * C + c;
            float v = (float)y[idx];
            y[idx] = (scalar_t)((v - mean) * coef + be);
        }
    }
}

template <typename scalar_t>
__global__ void bn_eval_kernel(scalar_t* __restrict__ y, const scalar_t* __restrict__ gamma, const scalar_t* __restrict__ beta,
                               const scalar_t* __restrict__ running_mean, const scalar_t* __restrict__ running_var,
                               int N, int C, float eps) {
    long total = (long)N*C;
    long idx = (long)blockIdx.x * blockDim.x + threadIdx.x;
    long stride = (long)gridDim.x * blockDim.x;
    for (long i=idx; i<total; i+=stride) {
        int c = (int)(i % C);
        float v = (float)y[i];
        float out = (v - (float)running_mean[c]) * rsqrtf((float)running_var[c] + eps) * (float)gamma[c] + (float)beta[c];
        y[i] = (scalar_t)out;
    }
}

void bn_train_cuda(torch::Tensor y, torch::Tensor bn_weight, torch::Tensor bn_bias, torch::Tensor running_mean, torch::Tensor running_var, double eps, double momentum) {
    int N = y.size(0), C = y.size(1);
    int chunks = (N + ROWS_PER_CHUNK - 1) / ROWS_PER_CHUNK;
    auto opts = y.options().dtype(torch::kFloat32);
    auto ps = torch::empty({chunks, C}, opts);
    auto pss = torch::empty({chunks, C}, opts);
    dim3 grid1((C + COLS - 1)/COLS, chunks);
    dim3 block(256);
    dim3 grid2((C + COLS - 1)/COLS, (N + NORM_ROWS - 1)/NORM_ROWS);
    AT_DISPATCH_FLOATING_TYPES(y.scalar_type(), "bn_train_cuda", [&] {
        partial_sum_kernel<scalar_t><<<grid1, block>>>(y.data_ptr<scalar_t>(), ps.data_ptr<float>(), pss.data_ptr<float>(), N, C, chunks);
        normalize_train_kernel<scalar_t><<<grid2, block>>>(y.data_ptr<scalar_t>(), ps.data_ptr<float>(), pss.data_ptr<float>(), bn_weight.data_ptr<scalar_t>(), bn_bias.data_ptr<scalar_t>(), running_mean.data_ptr<scalar_t>(), running_var.data_ptr<scalar_t>(), N, C, chunks, (float)eps, (float)momentum);
    });
}

void bn_eval_cuda(torch::Tensor y, torch::Tensor bn_weight, torch::Tensor bn_bias, torch::Tensor running_mean, torch::Tensor running_var, double eps) {
    int N=y.size(0), C=y.size(1); long total=(long)N*C; int threads=256; int blocks=(int)((total+threads-1)/threads); if(blocks>4096) blocks=4096;
    AT_DISPATCH_FLOATING_TYPES(y.scalar_type(), "bn_eval_cuda", [&] {
        bn_eval_kernel<scalar_t><<<blocks,threads>>>(y.data_ptr<scalar_t>(), bn_weight.data_ptr<scalar_t>(), bn_bias.data_ptr<scalar_t>(), running_mean.data_ptr<scalar_t>(), running_var.data_ptr<scalar_t>(), N, C, (float)eps);
    });
}
'''

_ext = load_inline(name='fused_linear_scale_bn_ext_v2', cpp_sources=cpp_source, cuda_sources=cuda_source,
                   functions=['forward'], extra_cflags=['-O3'], extra_cuda_cflags=['-O3'], verbose=False)

class ModelNew(nn.Module):
    def __init__(self, in_features, out_features, scale_shape, eps=1e-5, momentum=0.1):
        super(ModelNew, self).__init__()
        self.gemm = nn.Linear(in_features, out_features)
        self.scale = nn.Parameter(torch.randn(scale_shape))
        self.bn = nn.BatchNorm1d(out_features, eps=eps, momentum=momentum)
    def forward(self, x):
        return _ext.forward(x, self.gemm.weight, self.gemm.bias, self.scale,
                            self.bn.weight, self.bn.bias, self.bn.running_mean, self.bn.running_var,
                            self.training, float(self.bn.eps), float(self.bn.momentum))