gpt-5.5-priority · original · l123_single_gpt
compile_fail 39_Gemm_Scale_BatchNorm.py
model
gpt-5.5-priority
turns / max
1 / 1
tool calls
1
tokens (total)
0
wall clock
—
truncated
no
backend
cuda fp32
variant
original
runtime
—
ref runtime
-1.00 μs
speedup
—
started
2026-05-05T00:07:35Z
compile error: Error building extension 'fused_scale_bn_inline_ext_v1': [1/3] /opt/conda/bin/x86_64-conda-linux-gnu-c++ -MMD -MF main.o.d -DTORCH_EXTENSION_NAME=fused_scale_bn_inline_ext_v1 -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/l123_single_gpt/original/gpt-5.5-priority/level_2_problem_39_cache/k_
timingkernelbench.timing
kernel runtime
—
ref runtime
—
speedup
—
kernel mean / median / std
—
ref mean / median / std
—
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 · FINAL · 1 tool call(s) · 82.3s turn · 82.3s LLM · 15,157 chars (click to expand)
turn 082.3s turn · 82.3s LLM · 0.0s tools · FINAL
reasoning
**Considering output strategies** I’m thinking about calling linear functions and then applying custom reductions for each column, possibly outputting in-place. If I'm in eval mode, the batch normalization (BN) will use running mean and variance. The output will be calculated as linear times scale times the inverse standard deviation, then multiplied by gamma and added to bias. This seems straightforward and could allow for easy elementwise fusing, which should optimize the process! **Considering PyTorch output tolerance** I’m thinking about using PyTorch and considering the floating-point precision here. The accumulation seems to use fp32, and I believe a tolerance of 1e-4 for the output is reasonable. If the difference in the mean is around 1e-5, then that should be acceptable. I’m also pondering the use of a float sum tree for better efficiency, and I'm wondering if the scale could potentially be negative. Let's keep this in mind! **Calculating channel sums** I need to compute the sum per channel over \( N \)—that’s 16,384. Since \( C \) is set to 4,096, I’ll be coalescing these operations across \( C \). It seems straightforward, but I want to ensure I’m doing everything correctly in terms of efficiency and accuracy while processing each channel. I'll check my calculations to confirm that I'm on the right track!
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_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#define COL_TILE 32\n#define TY 8\n#define ROWS_PER_BLOCK 256\n\n__global__ void partial_sums_kernel(const float* __restrict__ y,\n float* __restrict__ psum,\n float* __restrict__ psq,\n int N, int C) {\n __shared__ float sh_sum[TY][COL_TILE];\n __shared__ float sh_sq[TY][COL_TILE];\n int tx = threadIdx.x;\n int ty = threadIdx.y;\n int c = blockIdx.x * COL_TILE + tx;\n int r0 = blockIdx.y * ROWS_PER_BLOCK;\n int rend = r0 + ROWS_PER_BLOCK;\n if (rend > N) rend = N;\n\n float s = 0.0f;\n float q = 0.0f;\n if (c < C) {\n for (int r = r0 + ty; r < rend; r += TY) {\n float v = y[(long long)r * C + c];\n s += v;\n q += v * v;\n }\n }\n sh_sum[ty][tx] = s;\n sh_sq[ty][tx] = q;\n __syncthreads();\n\n if (ty < 4) { sh_sum[ty][tx] += sh_sum[ty+4][tx]; sh_sq[ty][tx] += sh_sq[ty+4][tx]; }\n __syncthreads();\n if (ty < 2) { sh_sum[ty][tx] += sh_sum[ty+2][tx]; sh_sq[ty][tx] += sh_sq[ty+2][tx]; }\n __syncthreads();\n if (ty == 0) {\n s = sh_sum[0][tx] + sh_sum[1][tx];\n q = sh_sq[0][tx] + sh_sq[1][tx];\n if (c < C) {\n psum[(long long)blockIdx.y * C + c] = s;\n psq[(long long)blockIdx.y * C + c] = q;\n }\n }\n}\n\n__global__ void final_stats_kernel(const float* __restrict__ psum,\n const float* __restrict__ psq,\n const float* __restrict__ scale,\n float* __restrict__ running_mean,\n float* __restrict__ running_var,\n float* __restrict__ mean,\n float* __restrict__ invstd,\n int N, int C, int NB,\n float momentum, float eps) {\n int c = blockIdx.x;\n int t = threadIdx.x;\n __shared__ float ss[256];\n __shared__ float qq[256];\n float s = 0.0f;\n float q = 0.0f;\n for (int b = t; b < NB; b += blockDim.x) {\n s += psum[(long long)b * C + c];\n q += psq[(long long)b * C + c];\n }\n ss[t] = s;\n qq[t] = q;\n __syncthreads();\n for (int stride = blockDim.x >> 1; stride > 0; stride >>= 1) {\n if (t < stride) {\n ss[t] += ss[t + stride];\n qq[t] += qq[t + stride];\n }\n __syncthreads();\n }\n if (t == 0) {\n float invN = 1.0f / (float)N;\n float mz = ss[0] * invN;\n float vz = qq[0] * invN - mz * mz;\n if (vz < 0.0f) vz = 0.0f;\n float sc = scale[c];\n float m = mz * sc;\n float v = vz * sc * sc;\n mean[c] = m;\n invstd[c] = 1.0f / sqrtf(v + eps);\n float unbiased = v;\n if (N > 1) unbiased = v * ((float)N / (float)(N - 1));\n running_mean[c] = running_mean[c] * (1.0f - momentum) + momentum * m;\n running_var[c] = running_var[c] * (1.0f - momentum) + momentum * unbiased;\n }\n}\n\n__global__ void train_apply_kernel(float* __restrict__ y,\n const float* __restrict__ scale,\n const float* __restrict__ gamma,\n const float* __restrict__ beta,\n const float* __restrict__ mean,\n const float* __restrict__ invstd,\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 v = y[idx] * scale[c];\n y[idx] = (v - mean[c]) * invstd[c] * gamma[c] + beta[c];\n }\n}\n\n__global__ void eval_apply_kernel(float* __restrict__ y,\n const float* __restrict__ scale,\n const float* __restrict__ gamma,\n const float* __restrict__ beta,\n const float* __restrict__ running_mean,\n const float* __restrict__ running_var,\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 v = y[idx] * scale[c];\n float inv = 1.0f / sqrtf(running_var[c] + eps);\n y[idx] = (v - running_mean[c]) * inv * gamma[c] + beta[c];\n }\n}\n\ntorch::Tensor bn_train_inplace(torch::Tensor y,\n torch::Tensor scale,\n torch::Tensor gamma,\n torch::Tensor beta,\n torch::Tensor running_mean,\n torch::Tensor running_var,\n double momentum,\n double eps) {\n const int N = (int)y.size(0);\n const int C = (int)y.size(1);\n const int NB = (N + ROWS_PER_BLOCK - 1) / ROWS_PER_BLOCK;\n auto opts = y.options();\n torch::Tensor psum = torch::empty({NB, C}, opts);\n torch::Tensor psq = torch::empty({NB, C}, opts);\n torch::Tensor mean = torch::empty({C}, opts);\n torch::Tensor invstd = torch::empty({C}, opts);\n cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n dim3 block(COL_TILE, TY);\n dim3 grid((C + COL_TILE - 1) / COL_TILE, NB);\n partial_sums_kernel<<<grid, block, 0, stream>>>(y.data_ptr<float>(), psum.data_ptr<float>(), psq.data_ptr<float>(), N, C);\n final_stats_kernel<<<C, 256, 0, stream>>>(psum.data_ptr<float>(), psq.data_ptr<float>(), scale.data_ptr<float>(),\n running_mean.data_ptr<float>(), running_var.data_ptr<float>(), mean.data_ptr<float>(), invstd.data_ptr<float>(),\n N, C, NB, (float)momentum, (float)eps);\n long long total = (long long)N * C;\n int threads = 256;\n int blocks = (int)((total + threads - 1) / threads);\n if (blocks > 65535) blocks = 65535;\n train_apply_kernel<<<blocks, threads, 0, stream>>>(y.data_ptr<float>(), scale.data_ptr<float>(), gamma.data_ptr<float>(), beta.data_ptr<float>(), mean.data_ptr<float>(), invstd.data_ptr<float>(), total, C);\n return y;\n}\n\ntorch::Tensor bn_eval_inplace(torch::Tensor y,\n torch::Tensor scale,\n torch::Tensor gamma,\n torch::Tensor beta,\n torch::Tensor running_mean,\n torch::Tensor running_var,\n double eps) {\n const int N = (int)y.size(0);\n const int C = (int)y.size(1);\n long long total = (long long)N * C;\n int threads = 256;\n int blocks = (int)((total + threads - 1) / threads);\n if (blocks > 65535) blocks = 65535;\n cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n eval_apply_kernel<<<blocks, threads, 0, stream>>>(y.data_ptr<float>(), scale.data_ptr<float>(), gamma.data_ptr<float>(), beta.data_ptr<float>(), running_mean.data_ptr<float>(), running_var.data_ptr<float>(), total, C, (float)eps);\n return y;\n}\n'''\n\n_ext = load_inline(\n name='fused_scale_bn_inline_ext_v1',\n cpp_sources='',\n cuda_sources=_cuda_src,\n functions=['bn_train_inplace', 'bn_eval_inplace'],\n extra_cflags=['-O3'],\n extra_cuda_cflags=['-O3'],\n verbose=False,\n)\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\n def forward(self, x):\n y = F.linear(x, self.gemm.weight, self.gemm.bias)\n if self.bn.training:\n if self.bn.track_running_stats:\n self.bn.num_batches_tracked.add_(1)\n if self.bn.momentum is None:\n mom = 1.0 / float(self.bn.num_batches_tracked.item())\n else:\n mom = float(self.bn.momentum)\n else:\n mom = 0.0 if self.bn.momentum is None else float(self.bn.momentum)\n return _ext.bn_train_inplace(y, self.scale, self.bn.weight, self.bn.bias,\n self.bn.running_mean, self.bn.running_var,\n mom, float(self.bn.eps))\n else:\n return _ext.bn_eval_inplace(y, self.scale, self.bn.weight, self.bn.bias,\n self.bn.running_mean, self.bn.running_var,\n float(self.bn.eps))\n" }
result ← submit_kernel (fail)
failsubmit_kernel submit_kernel FAILED: kernel did not compile. Error building extension 'fused_scale_bn_inline_ext_v1': [1/3] /opt/conda/bin/x86_64-conda-linux-gnu-c++ -MMD -MF main.o.d -DTORCH_EXTENSION_NAME=fused_scale_bn_inline_ext_v1 -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/l123_single_gpt/original/gpt-5.5-priority/level_2_problem_39_cache/k_02cdfbe7962a/fused_scale_bn_inline_ext_v1/main.cpp -o main.o FAILED: [code=1] main.o /opt/conda/bin/x86_64-conda-linux-gnu-c++ -MMD -MF main.o.d -DTORCH_EXTENSION_NAME=fused_scale_bn_inline_ext_v1 -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/l123_single_gpt/original/gpt-5.5-priority/level_2_problem_39_cache/k_02cdfbe7962a/fused_scale_bn_inline_ext_v1/main.cpp -o main.o /scratch/abaner/KernelBench/runs/l123_single_gpt/original/gpt-5.5-priority/level_2_problem_39_cache/k_02cdfbe7962a/fused_scale_bn_inline_ext_v1/main.cpp: In function 'void pybind11_init_fused_scale_bn_inline_ext_v1(pybind11::module_&)': /scratch/abaner/KernelBench/runs/l123_single_gpt/original/gpt-5.5-priority/level_2_problem_39_cache/k_02cdfbe7962a/fused_scale_bn_inline_ext_v1/main.cpp:4:55: error: 'bn_train_inplace' was not declared in this scope 4 | m.def("bn_train_inplace", torch::wrap_pybind_function(bn_train_inplace), "bn_train_inplace"); | ^~~~~~~~~~~~~~~~ /scratch/abaner/KernelBench/runs/l123_single_gpt/original/gpt-5.5-priority/level_2_problem_39_cache/k_02cdfbe7962a/fused_scale_bn_inline_ext_v1/main.cpp:5:54: error: 'bn_eval_inplace' was not declared in this scope 5 | m.def("bn_eval_inplace", torch::wrap_pybind_function(bn_eval_inplace), "bn_eval_inplace"); | ^~~~~~~~~~~~~~~ [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_scale_bn_inline_ext_v1 -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/l123_single_gpt/original/gpt-5.5-priority/level_2_problem_39_cache/k_02cdfbe7962a/fused_scale_bn_inline_ext_v1/cuda.cu -o cuda.cuda.o nvcc warning : incompatible redefinition for option 'compiler-bindir', the last value of this option was used ninja: build stopped: subcommand failed.
final submitted kernel (220 lines, 9,098 chars) — full source, untruncated
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.cpp_extension import load_inline

_cuda_src = r'''
#include <torch/extension.h>
#include <ATen/cuda/CUDAContext.h>
#include <cuda.h>
#include <cuda_runtime.h>

#define COL_TILE 32
#define TY 8
#define ROWS_PER_BLOCK 256

__global__ void partial_sums_kernel(const float* __restrict__ y,
                                    float* __restrict__ psum,
                                    float* __restrict__ psq,
                                    int N, int C) {
    __shared__ float sh_sum[TY][COL_TILE];
    __shared__ float sh_sq[TY][COL_TILE];
    int tx = threadIdx.x;
    int ty = threadIdx.y;
    int c = blockIdx.x * COL_TILE + tx;
    int r0 = blockIdx.y * ROWS_PER_BLOCK;
    int rend = r0 + ROWS_PER_BLOCK;
    if (rend > N) rend = N;

    float s = 0.0f;
    float q = 0.0f;
    if (c < C) {
        for (int r = r0 + ty; r < rend; r += TY) {
            float v = y[(long long)r * C + c];
            s += v;
            q += v * v;
        }
    }
    sh_sum[ty][tx] = s;
    sh_sq[ty][tx] = q;
    __syncthreads();

    if (ty < 4) { sh_sum[ty][tx] += sh_sum[ty+4][tx]; sh_sq[ty][tx] += sh_sq[ty+4][tx]; }
    __syncthreads();
    if (ty < 2) { sh_sum[ty][tx] += sh_sum[ty+2][tx]; sh_sq[ty][tx] += sh_sq[ty+2][tx]; }
    __syncthreads();
    if (ty == 0) {
        s = sh_sum[0][tx] + sh_sum[1][tx];
        q = sh_sq[0][tx] + sh_sq[1][tx];
        if (c < C) {
            psum[(long long)blockIdx.y * C + c] = s;
            psq[(long long)blockIdx.y * C + c] = q;
        }
    }
}

__global__ void final_stats_kernel(const float* __restrict__ psum,
                                   const float* __restrict__ psq,
                                   const float* __restrict__ scale,
                                   float* __restrict__ running_mean,
                                   float* __restrict__ running_var,
                                   float* __restrict__ mean,
                                   float* __restrict__ invstd,
                                   int N, int C, int NB,
                                   float momentum, float eps) {
    int c = blockIdx.x;
    int t = threadIdx.x;
    __shared__ float ss[256];
    __shared__ float qq[256];
    float s = 0.0f;
    float q = 0.0f;
    for (int b = t; b < NB; b += blockDim.x) {
        s += psum[(long long)b * C + c];
        q += psq[(long long)b * C + c];
    }
    ss[t] = s;
    qq[t] = q;
    __syncthreads();
    for (int stride = blockDim.x >> 1; stride > 0; stride >>= 1) {
        if (t < stride) {
            ss[t] += ss[t + stride];
            qq[t] += qq[t + stride];
        }
        __syncthreads();
    }
    if (t == 0) {
        float invN = 1.0f / (float)N;
        float mz = ss[0] * invN;
        float vz = qq[0] * invN - mz * mz;
        if (vz < 0.0f) vz = 0.0f;
        float sc = scale[c];
        float m = mz * sc;
        float v = vz * sc * sc;
        mean[c] = m;
        invstd[c] = 1.0f / sqrtf(v + eps);
        float unbiased = v;
        if (N > 1) unbiased = v * ((float)N / (float)(N - 1));
        running_mean[c] = running_mean[c] * (1.0f - momentum) + momentum * m;
        running_var[c] = running_var[c] * (1.0f - momentum) + momentum * unbiased;
    }
}

__global__ void train_apply_kernel(float* __restrict__ y,
                                   const float* __restrict__ scale,
                                   const float* __restrict__ gamma,
                                   const float* __restrict__ beta,
                                   const float* __restrict__ mean,
                                   const float* __restrict__ invstd,
                                   long long total, int C) {
    long long idx = (long long)blockIdx.x * blockDim.x + threadIdx.x;
    long long stride = (long long)blockDim.x * gridDim.x;
    for (; idx < total; idx += stride) {
        int c = (int)(idx % C);
        float v = y[idx] * scale[c];
        y[idx] = (v - mean[c]) * invstd[c] * gamma[c] + beta[c];
    }
}

__global__ void eval_apply_kernel(float* __restrict__ y,
                                  const float* __restrict__ scale,
                                  const float* __restrict__ gamma,
                                  const float* __restrict__ beta,
                                  const float* __restrict__ running_mean,
                                  const float* __restrict__ running_var,
                                  long long total, int C, float eps) {
    long long idx = (long long)blockIdx.x * blockDim.x + threadIdx.x;
    long long stride = (long long)blockDim.x * gridDim.x;
    for (; idx < total; idx += stride) {
        int c = (int)(idx % C);
        float v = y[idx] * scale[c];
        float inv = 1.0f / sqrtf(running_var[c] + eps);
        y[idx] = (v - running_mean[c]) * inv * gamma[c] + beta[c];
    }
}

torch::Tensor bn_train_inplace(torch::Tensor y,
                               torch::Tensor scale,
                               torch::Tensor gamma,
                               torch::Tensor beta,
                               torch::Tensor running_mean,
                               torch::Tensor running_var,
                               double momentum,
                               double eps) {
    const int N = (int)y.size(0);
    const int C = (int)y.size(1);
    const int NB = (N + ROWS_PER_BLOCK - 1) / ROWS_PER_BLOCK;
    auto opts = y.options();
    torch::Tensor psum = torch::empty({NB, C}, opts);
    torch::Tensor psq = torch::empty({NB, C}, opts);
    torch::Tensor mean = torch::empty({C}, opts);
    torch::Tensor invstd = torch::empty({C}, opts);
    cudaStream_t stream = at::cuda::getCurrentCUDAStream();
    dim3 block(COL_TILE, TY);
    dim3 grid((C + COL_TILE - 1) / COL_TILE, NB);
    partial_sums_kernel<<<grid, block, 0, stream>>>(y.data_ptr<float>(), psum.data_ptr<float>(), psq.data_ptr<float>(), N, C);
    final_stats_kernel<<<C, 256, 0, stream>>>(psum.data_ptr<float>(), psq.data_ptr<float>(), scale.data_ptr<float>(),
        running_mean.data_ptr<float>(), running_var.data_ptr<float>(), mean.data_ptr<float>(), invstd.data_ptr<float>(),
        N, C, NB, (float)momentum, (float)eps);
    long long total = (long long)N * C;
    int threads = 256;
    int blocks = (int)((total + threads - 1) / threads);
    if (blocks > 65535) blocks = 65535;
    train_apply_kernel<<<blocks, threads, 0, stream>>>(y.data_ptr<float>(), scale.data_ptr<float>(), gamma.data_ptr<float>(), beta.data_ptr<float>(), mean.data_ptr<float>(), invstd.data_ptr<float>(), total, C);
    return y;
}

torch::Tensor bn_eval_inplace(torch::Tensor y,
                              torch::Tensor scale,
                              torch::Tensor gamma,
                              torch::Tensor beta,
                              torch::Tensor running_mean,
                              torch::Tensor running_var,
                              double eps) {
    const int N = (int)y.size(0);
    const int C = (int)y.size(1);
    long long total = (long long)N * C;
    int threads = 256;
    int blocks = (int)((total + threads - 1) / threads);
    if (blocks > 65535) blocks = 65535;
    cudaStream_t stream = at::cuda::getCurrentCUDAStream();
    eval_apply_kernel<<<blocks, threads, 0, stream>>>(y.data_ptr<float>(), scale.data_ptr<float>(), gamma.data_ptr<float>(), beta.data_ptr<float>(), running_mean.data_ptr<float>(), running_var.data_ptr<float>(), total, C, (float)eps);
    return y;
}
'''

_ext = load_inline(
    name='fused_scale_bn_inline_ext_v1',
    cpp_sources='',
    cuda_sources=_cuda_src,
    functions=['bn_train_inplace', 'bn_eval_inplace'],
    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):
        y = F.linear(x, self.gemm.weight, self.gemm.bias)
        if self.bn.training:
            if self.bn.track_running_stats:
                self.bn.num_batches_tracked.add_(1)
                if self.bn.momentum is None:
                    mom = 1.0 / float(self.bn.num_batches_tracked.item())
                else:
                    mom = float(self.bn.momentum)
            else:
                mom = 0.0 if self.bn.momentum is None else float(self.bn.momentum)
            return _ext.bn_train_inplace(y, self.scale, self.bn.weight, self.bn.bias,
                                         self.bn.running_mean, self.bn.running_var,
                                         mom, float(self.bn.eps))
        else:
            return _ext.bn_eval_inplace(y, self.scale, self.bn.weight, self.bn.bias,
                                        self.bn.running_mean, self.bn.running_var,
                                        float(self.bn.eps))