gpt-5.5-priority · original · l123_single_gpt
correct 84_Gemm_BatchNorm_Scaling_Softmax.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
2.77 μs
ref runtime
2.81 μs
speedup
1.01x
started
2026-05-05T00:44:58Z
timingkernelbench.timing
kernel runtime
2.77 μs
ref runtime
2.81 μs
speedup
1.01x
kernel mean / median / std
2.77 / — / 0.01 μs
ref mean / median / std
2.81 / — / 0.00 μs
numerical precisioneval_kernel_against_ref
max abs error
2.26e-07
mean abs error
4.18e-11
max rel error
3.81e-05
mean rel error
2.86e-07
gpu memoryextended_metrics.measure_memory
kernel peak
641.38 MB
ref peak
641.38 MB
kernel / ref
1.00x
kernel launches / fusiontorch.profiler
kernel launches
10
ref launches
17
fusion ratio (ref / kernel)
1.70x
total cuda time
5646 μs
energyNVML (pynvml)
kernel mJ / run
241.44
ref mJ / run
241.70
ratio (ref / kernel)
1.00x
avg power
87.09 W
ref avg power
85.99 W
SOL (speed-of-light)nsight_hardware_counters
SOL score (0–1)
1.000
dram util %
86.1%
compute util %
100.0%
bottleneck
compute-bound
dominant pipe
FP32
arithmetic intensity
—
ridge point
20.00
roofline / occupancynsight_hardware_counters
dram bandwidth
2883.10 GB/s
dram util %
86.1%
fp32 tflops
—
fp32 util %
—
fp16 tflops
—
occupancy %
115.1%
bottleneck
compute-bound
dominant pipe %
106.4%
arithmetic intensity
—
ridge point
20.00
L1 hit %
—
L2 hit %
247.6%
ld sectors / req
12.99
st sectors / req
—
regs / thread
350
smem / block
40960 B
block size
768
peak bw
3350.00 GB/s
peak fp32 tflops
67.00
peak fp16 tflops
1979.00
warp stalls (top 5)nsight
long_scoreboard
5704.4%
short_scoreboard
145.2%
mio_throttle
29.6%
reference PyTorch source (the prompt the agent sees)
import torch
import torch.nn as nn

class Model(nn.Module):
    """
    Model that performs a matrix multiplication (Gemm), Batch Normalization, scaling, and Softmax.
    """
    def __init__(self, in_features, out_features, bn_eps=1e-5, bn_momentum=0.1, scale_shape=(1,)):
        super(Model, self).__init__()
        self.gemm = nn.Linear(in_features, out_features)
        self.bn = nn.BatchNorm1d(out_features, eps=bn_eps, momentum=bn_momentum)
        self.scale = nn.Parameter(torch.ones(scale_shape))
        self.softmax = nn.Softmax(dim=1)

    def forward(self, x):
        """
        Args:
            x (torch.Tensor): Input tensor of shape (batch_size, in_features).
        Returns:
            torch.Tensor: Output tensor of shape (batch_size, out_features).
        """
        x = self.gemm(x)
        x = self.bn(x)
        x = self.scale * x
        x = self.softmax(x)
        return x

batch_size = 1024
in_features = 8192
out_features = 8192
bn_eps = 1e-5
bn_momentum = 0.1
scale_shape = (1,)

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

def get_init_inputs():
    return [in_features, out_features, bn_eps, bn_momentum, scale_shape]
turn 0 · FINAL · 1 tool call(s) · 51.8s turn · 51.8s LLM · 12,696 chars (click to expand)
turn 051.8s turn · 51.8s LLM · 0.0s tools · FINAL
reasoning
**Analyzing data performance** I’m looking at a data size of around 8 million, which is relatively small compared to GEMM operations. Softmax processing also involves 8 million units. Then there’s the overhead from Batch Normalization plus softmax in PyTorch, which could add several kernel calls. I wonder if a custom fused layer could alleviate some of this. However, I must remember that for training, updating running statistics is essential. It's a bit of a balancing act! **Calculating output values** I'm processing a loop for each row and its columns. First, I'm computing the value of z using the formula z = (y - mean) * invstd * gamma + beta, and then applying a scaling factor. After that, I need to find the maximum value, sum the exponential results, and finally write the output. I'm considering if I need to reread the y values again, but it seems like I just need to do that once. **Evaluating training output** I'm thinking about the running state and how it relates to correctness. If the number of batches being processed matches, then that’s a good sign, right? But what happens if the output training? I’m realizing that running an update may not influence the output at all. I need to dig deeper into how these updates connect with what’s actually being produced to ensure everything is functioning properly.
call → submit_kernel
callsubmit_kernel { "kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\n_cpp_src = r'''\n#include <torch/extension.h>\n\ntorch::Tensor bn_softmax_cuda(torch::Tensor x,\n torch::Tensor gamma,\n torch::Tensor beta,\n torch::Tensor running_mean,\n torch::Tensor running_var,\n torch::Tensor scale,\n double eps,\n double momentum,\n bool training);\n\ntorch::Tensor bn_softmax_forward(torch::Tensor x,\n torch::Tensor gamma,\n torch::Tensor beta,\n torch::Tensor running_mean,\n torch::Tensor running_var,\n torch::Tensor scale,\n double eps,\n double momentum,\n bool training) {\n return bn_softmax_cuda(x, gamma, beta, running_mean, running_var, scale, eps, momentum, training);\n}\n'''\n\n_cuda_src = r'''\n#include <torch/extension.h>\n#include <ATen/cuda/CUDAContext.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <cfloat>\n\n__global__ void bn_stats_kernel(const float* __restrict__ x,\n float* __restrict__ mean,\n float* __restrict__ invstd,\n float* __restrict__ running_mean,\n float* __restrict__ running_var,\n int N, int C, float eps, float momentum) {\n int tx = threadIdx.x; // column inside tile, 0..31\n int ty = threadIdx.y; // reducer lane over rows, 0..7\n int col = blockIdx.x * 32 + tx;\n\n float s = 0.0f;\n float q = 0.0f;\n if (col < C) {\n for (int r = ty; r < N; r += 8) {\n float v = x[(long long)r * C + col];\n s += v;\n q += v * v;\n }\n }\n\n __shared__ float sh_s[8][32];\n __shared__ float sh_q[8][32];\n sh_s[ty][tx] = s;\n sh_q[ty][tx] = q;\n __syncthreads();\n\n if (ty == 0 && col < C) {\n float sum = 0.0f;\n float ssq = 0.0f;\n #pragma unroll\n for (int k = 0; k < 8; ++k) {\n sum += sh_s[k][tx];\n ssq += sh_q[k][tx];\n }\n float m = sum / (float)N;\n float var = ssq / (float)N - m * m;\n var = var < 0.0f ? 0.0f : var;\n mean[col] = m;\n invstd[col] = rsqrtf(var + eps);\n\n float unbiased = var * ((float)N / (float)(N - 1));\n running_mean[col] = running_mean[col] * (1.0f - momentum) + momentum * m;\n running_var[col] = running_var[col] * (1.0f - momentum) + momentum * unbiased;\n }\n}\n\n__device__ __forceinline__ float block_reduce_max(float v, float* smem) {\n int tid = threadIdx.x;\n smem[tid] = v;\n __syncthreads();\n for (int stride = blockDim.x >> 1; stride > 0; stride >>= 1) {\n if (tid < stride) {\n float other = smem[tid + stride];\n smem[tid] = smem[tid] > other ? smem[tid] : other;\n }\n __syncthreads();\n }\n return smem[0];\n}\n\n__device__ __forceinline__ float block_reduce_sum(float v, float* smem) {\n int tid = threadIdx.x;\n smem[tid] = v;\n __syncthreads();\n for (int stride = blockDim.x >> 1; stride > 0; stride >>= 1) {\n if (tid < stride) smem[tid] += smem[tid + stride];\n __syncthreads();\n }\n return smem[0];\n}\n\n__global__ void softmax_train_kernel(const float* __restrict__ x,\n const float* __restrict__ gamma,\n const float* __restrict__ beta,\n const float* __restrict__ mean,\n const float* __restrict__ invstd,\n const float* __restrict__ scale,\n float* __restrict__ out,\n int N, int C) {\n int row = blockIdx.x;\n int tid = threadIdx.x;\n float sc = scale[0];\n extern __shared__ float smem[];\n\n float mx = -FLT_MAX;\n long long base = (long long)row * C;\n for (int c = tid; c < C; c += blockDim.x) {\n float z = ((x[base + c] - mean[c]) * invstd[c] * gamma[c] + beta[c]) * sc;\n mx = mx > z ? mx : z;\n }\n mx = block_reduce_max(mx, smem);\n\n float ss = 0.0f;\n for (int c = tid; c < C; c += blockDim.x) {\n float z = ((x[base + c] - mean[c]) * invstd[c] * gamma[c] + beta[c]) * sc;\n ss += __expf(z - mx);\n }\n ss = block_reduce_sum(ss, smem);\n float inv = 1.0f / ss;\n\n for (int c = tid; c < C; c += blockDim.x) {\n float z = ((x[base + c] - mean[c]) * invstd[c] * gamma[c] + beta[c]) * sc;\n out[base + c] = __expf(z - mx) * inv;\n }\n}\n\n__global__ void softmax_eval_kernel(const float* __restrict__ x,\n const float* __restrict__ gamma,\n const float* __restrict__ beta,\n const float* __restrict__ running_mean,\n const float* __restrict__ running_var,\n const float* __restrict__ scale,\n float* __restrict__ out,\n int N, int C, float eps) {\n int row = blockIdx.x;\n int tid = threadIdx.x;\n float sc = scale[0];\n extern __shared__ float smem[];\n\n float mx = -FLT_MAX;\n long long base = (long long)row * C;\n for (int c = tid; c < C; c += blockDim.x) {\n float z = ((x[base + c] - running_mean[c]) * rsqrtf(running_var[c] + eps) * gamma[c] + beta[c]) * sc;\n mx = mx > z ? mx : z;\n }\n mx = block_reduce_max(mx, smem);\n\n float ss = 0.0f;\n for (int c = tid; c < C; c += blockDim.x) {\n float z = ((x[base + c] - running_mean[c]) * rsqrtf(running_var[c] + eps) * gamma[c] + beta[c]) * sc;\n ss += __expf(z - mx);\n }\n ss = block_reduce_sum(ss, smem);\n float inv = 1.0f / ss;\n\n for (int c = tid; c < C; c += blockDim.x) {\n float z = ((x[base + c] - running_mean[c]) * rsqrtf(running_var[c] + eps) * gamma[c] + beta[c]) * sc;\n out[base + c] = __expf(z - mx) * inv;\n }\n}\n\ntorch::Tensor bn_softmax_cuda(torch::Tensor x,\n torch::Tensor gamma,\n torch::Tensor beta,\n torch::Tensor running_mean,\n torch::Tensor running_var,\n torch::Tensor scale,\n double eps,\n double momentum,\n bool training) {\n const int N = (int)x.size(0);\n const int C = (int)x.size(1);\n auto out = torch::empty_like(x);\n cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n\n const int threads = 256;\n const size_t shmem = threads * sizeof(float);\n\n if (training) {\n auto mean = torch::empty({C}, x.options());\n auto invstd = torch::empty({C}, x.options());\n dim3 block_stats(32, 8);\n dim3 grid_stats((C + 31) / 32);\n bn_stats_kernel<<<grid_stats, block_stats, 0, stream>>>(\n x.data_ptr<float>(), mean.data_ptr<float>(), invstd.data_ptr<float>(),\n running_mean.data_ptr<float>(), running_var.data_ptr<float>(),\n N, C, (float)eps, (float)momentum);\n softmax_train_kernel<<<N, threads, shmem, stream>>>(\n x.data_ptr<float>(), gamma.data_ptr<float>(), beta.data_ptr<float>(),\n mean.data_ptr<float>(), invstd.data_ptr<float>(), scale.data_ptr<float>(),\n out.data_ptr<float>(), N, C);\n } else {\n softmax_eval_kernel<<<N, threads, shmem, stream>>>(\n x.data_ptr<float>(), gamma.data_ptr<float>(), beta.data_ptr<float>(),\n running_mean.data_ptr<float>(), running_var.data_ptr<float>(), scale.data_ptr<float>(),\n out.data_ptr<float>(), N, C, (float)eps);\n }\n C10_CUDA_KERNEL_LAUNCH_CHECK();\n return out;\n}\n'''\n\n_ext = load_inline(\n name='gemm_bn_scale_softmax_ext_v1',\n cpp_sources=_cpp_src,\n cuda_sources=_cuda_src,\n functions=['bn_softmax_forward'],\n extra_cflags=['-O3'],\n extra_cuda_cflags=['-O3', '--use_fast_math'],\n verbose=False,\n)\n\nclass ModelNew(nn.Module):\n \"\"\"Fused replacement for Linear -> BatchNorm1d -> scalar scale -> Softmax.\"\"\"\n def __init__(self, in_features, out_features, bn_eps=1e-5, bn_momentum=0.1, scale_shape=(1,)):\n super(ModelNew, self).__init__()\n self.gemm = nn.Linear(in_features, out_features)\n self.bn = nn.BatchNorm1d(out_features, eps=bn_eps, momentum=bn_momentum)\n self.scale = nn.Parameter(torch.ones(scale_shape))\n\n def forward(self, x):\n y = torch.nn.functional.linear(x, self.gemm.weight, self.gemm.bias)\n training = bool(self.training and self.bn.training and self.bn.track_running_stats)\n if training:\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 = float(self.bn.momentum if self.bn.momentum is not None else 0.0)\n return _ext.bn_softmax_forward(y, self.bn.weight, self.bn.bias,\n self.bn.running_mean, self.bn.running_var,\n self.scale, float(self.bn.eps), mom, training)\n" }
result ← submit_kernel (ok)
oksubmit_kernel submit_kernel PASSED: (5 / 5) correctness trials all passed. Kernel runtime: 2.77 μs Runtime stats: mean=2.77μs median=?μs std=0.01μs Numerical precision: max_abs_err=2.26e-07 mean_abs_err=4.18e-11 max_rel_err=3.81e-05 Memory: 641.38 MB peak (ref: 641.38 MB, ratio: 1.0x) Kernel launches: 10 (ref: 17, fusion ratio: 1.7) Energy: 241.44 mJ/run (ref: 241.7 mJ, ratio: 1.0011x) SOL score: 1.0
final submitted kernel (252 lines, 9,666 chars) — full source, untruncated
import torch
import torch.nn as nn
from torch.utils.cpp_extension import load_inline

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

torch::Tensor bn_softmax_cuda(torch::Tensor x,
                              torch::Tensor gamma,
                              torch::Tensor beta,
                              torch::Tensor running_mean,
                              torch::Tensor running_var,
                              torch::Tensor scale,
                              double eps,
                              double momentum,
                              bool training);

torch::Tensor bn_softmax_forward(torch::Tensor x,
                                  torch::Tensor gamma,
                                  torch::Tensor beta,
                                  torch::Tensor running_mean,
                                  torch::Tensor running_var,
                                  torch::Tensor scale,
                                  double eps,
                                  double momentum,
                                  bool training) {
    return bn_softmax_cuda(x, gamma, beta, running_mean, running_var, scale, eps, momentum, training);
}
'''

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

__global__ void bn_stats_kernel(const float* __restrict__ x,
                                float* __restrict__ mean,
                                float* __restrict__ invstd,
                                float* __restrict__ running_mean,
                                float* __restrict__ running_var,
                                int N, int C, float eps, float momentum) {
    int tx = threadIdx.x;   // column inside tile, 0..31
    int ty = threadIdx.y;   // reducer lane over rows, 0..7
    int col = blockIdx.x * 32 + tx;

    float s = 0.0f;
    float q = 0.0f;
    if (col < C) {
        for (int r = ty; r < N; r += 8) {
            float v = x[(long long)r * C + col];
            s += v;
            q += v * v;
        }
    }

    __shared__ float sh_s[8][32];
    __shared__ float sh_q[8][32];
    sh_s[ty][tx] = s;
    sh_q[ty][tx] = q;
    __syncthreads();

    if (ty == 0 && col < C) {
        float sum = 0.0f;
        float ssq = 0.0f;
        #pragma unroll
        for (int k = 0; k < 8; ++k) {
            sum += sh_s[k][tx];
            ssq += sh_q[k][tx];
        }
        float m = sum / (float)N;
        float var = ssq / (float)N - m * m;
        var = var < 0.0f ? 0.0f : var;
        mean[col] = m;
        invstd[col] = rsqrtf(var + eps);

        float unbiased = var * ((float)N / (float)(N - 1));
        running_mean[col] = running_mean[col] * (1.0f - momentum) + momentum * m;
        running_var[col]  = running_var[col]  * (1.0f - momentum) + momentum * unbiased;
    }
}

__device__ __forceinline__ float block_reduce_max(float v, float* smem) {
    int tid = threadIdx.x;
    smem[tid] = v;
    __syncthreads();
    for (int stride = blockDim.x >> 1; stride > 0; stride >>= 1) {
        if (tid < stride) {
            float other = smem[tid + stride];
            smem[tid] = smem[tid] > other ? smem[tid] : other;
        }
        __syncthreads();
    }
    return smem[0];
}

__device__ __forceinline__ float block_reduce_sum(float v, float* smem) {
    int tid = threadIdx.x;
    smem[tid] = v;
    __syncthreads();
    for (int stride = blockDim.x >> 1; stride > 0; stride >>= 1) {
        if (tid < stride) smem[tid] += smem[tid + stride];
        __syncthreads();
    }
    return smem[0];
}

__global__ void softmax_train_kernel(const float* __restrict__ x,
                                     const float* __restrict__ gamma,
                                     const float* __restrict__ beta,
                                     const float* __restrict__ mean,
                                     const float* __restrict__ invstd,
                                     const float* __restrict__ scale,
                                     float* __restrict__ out,
                                     int N, int C) {
    int row = blockIdx.x;
    int tid = threadIdx.x;
    float sc = scale[0];
    extern __shared__ float smem[];

    float mx = -FLT_MAX;
    long long base = (long long)row * C;
    for (int c = tid; c < C; c += blockDim.x) {
        float z = ((x[base + c] - mean[c]) * invstd[c] * gamma[c] + beta[c]) * sc;
        mx = mx > z ? mx : z;
    }
    mx = block_reduce_max(mx, smem);

    float ss = 0.0f;
    for (int c = tid; c < C; c += blockDim.x) {
        float z = ((x[base + c] - mean[c]) * invstd[c] * gamma[c] + beta[c]) * sc;
        ss += __expf(z - mx);
    }
    ss = block_reduce_sum(ss, smem);
    float inv = 1.0f / ss;

    for (int c = tid; c < C; c += blockDim.x) {
        float z = ((x[base + c] - mean[c]) * invstd[c] * gamma[c] + beta[c]) * sc;
        out[base + c] = __expf(z - mx) * inv;
    }
}

__global__ void softmax_eval_kernel(const float* __restrict__ x,
                                    const float* __restrict__ gamma,
                                    const float* __restrict__ beta,
                                    const float* __restrict__ running_mean,
                                    const float* __restrict__ running_var,
                                    const float* __restrict__ scale,
                                    float* __restrict__ out,
                                    int N, int C, float eps) {
    int row = blockIdx.x;
    int tid = threadIdx.x;
    float sc = scale[0];
    extern __shared__ float smem[];

    float mx = -FLT_MAX;
    long long base = (long long)row * C;
    for (int c = tid; c < C; c += blockDim.x) {
        float z = ((x[base + c] - running_mean[c]) * rsqrtf(running_var[c] + eps) * gamma[c] + beta[c]) * sc;
        mx = mx > z ? mx : z;
    }
    mx = block_reduce_max(mx, smem);

    float ss = 0.0f;
    for (int c = tid; c < C; c += blockDim.x) {
        float z = ((x[base + c] - running_mean[c]) * rsqrtf(running_var[c] + eps) * gamma[c] + beta[c]) * sc;
        ss += __expf(z - mx);
    }
    ss = block_reduce_sum(ss, smem);
    float inv = 1.0f / ss;

    for (int c = tid; c < C; c += blockDim.x) {
        float z = ((x[base + c] - running_mean[c]) * rsqrtf(running_var[c] + eps) * gamma[c] + beta[c]) * sc;
        out[base + c] = __expf(z - mx) * inv;
    }
}

torch::Tensor bn_softmax_cuda(torch::Tensor x,
                              torch::Tensor gamma,
                              torch::Tensor beta,
                              torch::Tensor running_mean,
                              torch::Tensor running_var,
                              torch::Tensor scale,
                              double eps,
                              double momentum,
                              bool training) {
    const int N = (int)x.size(0);
    const int C = (int)x.size(1);
    auto out = torch::empty_like(x);
    cudaStream_t stream = at::cuda::getCurrentCUDAStream();

    const int threads = 256;
    const size_t shmem = threads * sizeof(float);

    if (training) {
        auto mean = torch::empty({C}, x.options());
        auto invstd = torch::empty({C}, x.options());
        dim3 block_stats(32, 8);
        dim3 grid_stats((C + 31) / 32);
        bn_stats_kernel<<<grid_stats, block_stats, 0, stream>>>(
            x.data_ptr<float>(), mean.data_ptr<float>(), invstd.data_ptr<float>(),
            running_mean.data_ptr<float>(), running_var.data_ptr<float>(),
            N, C, (float)eps, (float)momentum);
        softmax_train_kernel<<<N, threads, shmem, stream>>>(
            x.data_ptr<float>(), gamma.data_ptr<float>(), beta.data_ptr<float>(),
            mean.data_ptr<float>(), invstd.data_ptr<float>(), scale.data_ptr<float>(),
            out.data_ptr<float>(), N, C);
    } else {
        softmax_eval_kernel<<<N, threads, shmem, stream>>>(
            x.data_ptr<float>(), gamma.data_ptr<float>(), beta.data_ptr<float>(),
            running_mean.data_ptr<float>(), running_var.data_ptr<float>(), scale.data_ptr<float>(),
            out.data_ptr<float>(), N, C, (float)eps);
    }
    C10_CUDA_KERNEL_LAUNCH_CHECK();
    return out;
}
'''

_ext = load_inline(
    name='gemm_bn_scale_softmax_ext_v1',
    cpp_sources=_cpp_src,
    cuda_sources=_cuda_src,
    functions=['bn_softmax_forward'],
    extra_cflags=['-O3'],
    extra_cuda_cflags=['-O3', '--use_fast_math'],
    verbose=False,
)

class ModelNew(nn.Module):
    """Fused replacement for Linear -> BatchNorm1d -> scalar scale -> Softmax."""
    def __init__(self, in_features, out_features, bn_eps=1e-5, bn_momentum=0.1, scale_shape=(1,)):
        super(ModelNew, self).__init__()
        self.gemm = nn.Linear(in_features, out_features)
        self.bn = nn.BatchNorm1d(out_features, eps=bn_eps, momentum=bn_momentum)
        self.scale = nn.Parameter(torch.ones(scale_shape))

    def forward(self, x):
        y = torch.nn.functional.linear(x, self.gemm.weight, self.gemm.bias)
        training = bool(self.training and self.bn.training and self.bn.track_running_stats)
        if training:
            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 = float(self.bn.momentum if self.bn.momentum is not None else 0.0)
        return _ext.bn_softmax_forward(y, self.bn.weight, self.bn.bias,
                                       self.bn.running_mean, self.bn.running_var,
                                       self.scale, float(self.bn.eps), mom, training)