gpt-5.5-priority · original · l123_single_gpt
correct 97_Matmul_BatchNorm_BiasAdd_Divide_Swish.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.75 μs
ref runtime
2.85 μs
speedup
1.04x
started
2026-05-05T00:01:57Z
timingkernelbench.timing
kernel runtime
2.75 μs
ref runtime
2.85 μs
speedup
1.04x
kernel mean / median / std
2.75 / — / 0.01 μs
ref mean / median / std
2.85 / — / 0.01 μs
numerical precisioneval_kernel_against_ref
max abs error
0.00
mean abs error
0.00
max rel error
0.00
mean rel error
0.00
gpu memoryextended_metrics.measure_memory
kernel peak
673.31 MB
ref peak
673.31 MB
kernel / ref
1.00x
kernel launches / fusiontorch.profiler
kernel launches
15
ref launches
18
fusion ratio (ref / kernel)
1.20x
total cuda time
5986 μs
energyNVML (pynvml)
kernel mJ / run
483.42
ref mJ / run
241.92
ratio (ref / kernel)
0.50x
avg power
174.73 W
ref avg power
84.88 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
5607.65 GB/s
dram util %
100.0%
fp32 tflops
—
fp32 util %
—
fp16 tflops
—
occupancy %
227.4%
bottleneck
compute-bound
dominant pipe %
135.1%
arithmetic intensity
—
ridge point
20.00
L1 hit %
—
L2 hit %
282.0%
ld sectors / req
18.50
st sectors / req
—
regs / thread
400
smem / block
45184 B
block size
1536
peak bw
3350.00 GB/s
peak fp32 tflops
67.00
peak fp16 tflops
1979.00
warp stalls (top 5)nsight
long_scoreboard
7319.0%
short_scoreboard
575.9%
mio_throttle
146.8%
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, batch normalization, bias addition, division, and Swish activation.
    """
    def __init__(self, in_features, out_features, bn_eps=1e-5, bn_momentum=0.1, bias_shape=(1,), divide_value=1.0):
        super(Model, self).__init__()
        self.matmul = nn.Linear(in_features, out_features)
        self.bn = nn.BatchNorm1d(out_features, eps=bn_eps, momentum=bn_momentum)
        self.bias = nn.Parameter(torch.randn(bias_shape))
        self.divide_value = divide_value

    def forward(self, x):
        x = self.matmul(x)
        x = self.bn(x)
        x = x + self.bias
        x = x / self.divide_value
        x = x * torch.sigmoid(x)
        return x

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

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

def get_init_inputs():
    return [in_features, out_features, bn_eps, bn_momentum, bias_shape, divide_value]
turn 0 · FINAL · 1 tool call(s) · 60.1s turn · 60.1s LLM · 7,326 chars (click to expand)
turn 060.1s turn · 60.1s LLM · 0.0s tools · FINAL
reasoning
**Considering optimized reductions** I'm focusing on PyTorch's batch normalization and its use of optimized reductions. I think our simple strided reduction could be a bit slow, since it involves 8192 columns and 1024 loads, which amounts to 8 million uncoalesced transactions. I’m wondering if that might still be manageable. PyTorch's batch normalization might only take around 0.1 milliseconds, so maybe we're in the right ballpark. It's an interesting comparison for performance! **Considering compilation issues** I'm noting that NVCC might compile heavy. That suggests it could be handling larger workloads or code complexities. I wonder if there are specific optimizations I could explore to streamline the compilation process. Maybe looking into parallel compilation or reducing dependency sizes could help? It's interesting to think about how different configurations might influence performance. I'll need to dig deeper to see if this heaviness is common or if there are ways to alleviate it.
call → submit_kernel
callsubmit_kernel { "kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\n\n_cpp_src = r'''\n#include <torch/extension.h>\n\ntorch::Tensor fused_forward(torch::Tensor input,\n torch::Tensor weight,\n torch::Tensor lin_bias,\n torch::Tensor bn_weight,\n torch::Tensor bn_bias,\n torch::Tensor running_mean,\n torch::Tensor running_var,\n torch::Tensor extra_bias,\n bool training,\n double eps,\n double momentum,\n double divide_value);\n'''\n\n_cuda_src = r'''\n#include <torch/extension.h>\n#include <ATen/ATen.h>\n#include <ATen/ops/linear.h>\n#include <ATen/ops/batch_norm.h>\n#include <ATen/cuda/CUDAContext.h>\n#include <c10/cuda/CUDAException.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n\n__global__ void bias_swish_kernel(const float* __restrict__ in,\n const float* __restrict__ bias,\n float* __restrict__ out,\n long long total,\n int C,\n long long bias_numel,\n float inv_div) {\n long long idx = (long long)blockIdx.x * blockDim.x + threadIdx.x;\n if (idx >= total) return;\n\n float b;\n if (bias_numel == 1) {\n b = bias[0];\n } else if (bias_numel == (long long)C) {\n b = bias[idx % C];\n } else if (bias_numel == total) {\n b = bias[idx];\n } else {\n // Covers the only broadcast patterns used by this benchmark (scalar) and\n // common (C,) / full-size cases. For unusual shapes, fall back to a\n // cyclic index rather than reading out of bounds.\n b = bias[idx % bias_numel];\n }\n\n float x = (in[idx] + b) * inv_div;\n float s = 1.0f / (1.0f + expf(-x));\n out[idx] = x * s;\n}\n\ntorch::Tensor fused_forward(torch::Tensor input,\n torch::Tensor weight,\n torch::Tensor lin_bias,\n torch::Tensor bn_weight,\n torch::Tensor bn_bias,\n torch::Tensor running_mean,\n torch::Tensor running_var,\n torch::Tensor extra_bias,\n bool training,\n double eps,\n double momentum,\n double divide_value) {\n // Keep the numerically sensitive / highly optimized GEMM and BatchNorm in\n // ATen/cuDNN, and fuse the remaining bias/divide/Swish chain into one CUDA\n // pass. This removes several full-tensor PyTorch kernels from the forward.\n auto y = at::linear(input, weight, lin_bias);\n auto z = at::batch_norm(y, bn_weight, bn_bias, running_mean, running_var,\n training, momentum, eps, true);\n\n auto out = torch::empty_like(z);\n const long long total = z.numel();\n if (total == 0) return out;\n\n const int C = (z.dim() >= 2) ? (int)z.size(1) : (int)total;\n const int threads = 256;\n const int blocks = (int)((total + threads - 1) / threads);\n float inv_div = 1.0f / static_cast<float>(divide_value);\n\n bias_swish_kernel<<<blocks, threads, 0, at::cuda::getCurrentCUDAStream()>>>(\n z.data_ptr<float>(), extra_bias.data_ptr<float>(), out.data_ptr<float>(),\n total, C, extra_bias.numel(), inv_div);\n C10_CUDA_KERNEL_LAUNCH_CHECK();\n return out;\n}\n'''\n\n_ext = load_inline(\n name=\"bn_linear_swish_fused_ext_v1\",\n cpp_sources=_cpp_src,\n cuda_sources=_cuda_src,\n functions=[\"fused_forward\"],\n extra_cflags=[\"-O3\"],\n extra_cuda_cflags=[\"-O3\"],\n verbose=False,\n)\n\n\nclass ModelNew(nn.Module):\n \"\"\"\n Drop-in replacement for the reference module. Parameters and buffers are\n named identically so state_dict copying works unchanged.\n \"\"\"\n def __init__(self, in_features, out_features, bn_eps=1e-5, bn_momentum=0.1, bias_shape=(1,), divide_value=1.0):\n super(ModelNew, self).__init__()\n self.matmul = nn.Linear(in_features, out_features)\n self.bn = nn.BatchNorm1d(out_features, eps=bn_eps, momentum=bn_momentum)\n self.bias = nn.Parameter(torch.randn(bias_shape))\n self.divide_value = divide_value\n\n def forward(self, x):\n return _ext.fused_forward(\n x,\n self.matmul.weight,\n self.matmul.bias,\n self.bn.weight,\n self.bn.bias,\n self.bn.running_mean,\n self.bn.running_var,\n self.bias,\n self.bn.training,\n float(self.bn.eps),\n float(self.bn.momentum if self.bn.momentum is not None else 0.1),\n float(self.divide_value),\n )\n" }
result ← submit_kernel (ok)
oksubmit_kernel submit_kernel PASSED: (5 / 5) correctness trials all passed. Kernel runtime: 2.75 μs Runtime stats: mean=2.75μs median=?μs std=0.01μs Numerical precision: max_abs_err=0.00e+00 mean_abs_err=0.00e+00 max_rel_err=0.00e+00 Memory: 673.31 MB peak (ref: 673.31 MB, ratio: 1.0x) Kernel launches: 15 (ref: 18, fusion ratio: 1.2) Energy: 483.42 mJ/run (ref: 241.92 mJ, ratio: 0.5004x) SOL score: 1.0
final submitted kernel (136 lines, 4,925 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 fused_forward(torch::Tensor input,
                           torch::Tensor weight,
                           torch::Tensor lin_bias,
                           torch::Tensor bn_weight,
                           torch::Tensor bn_bias,
                           torch::Tensor running_mean,
                           torch::Tensor running_var,
                           torch::Tensor extra_bias,
                           bool training,
                           double eps,
                           double momentum,
                           double divide_value);
'''

_cuda_src = r'''
#include <torch/extension.h>
#include <ATen/ATen.h>
#include <ATen/ops/linear.h>
#include <ATen/ops/batch_norm.h>
#include <ATen/cuda/CUDAContext.h>
#include <c10/cuda/CUDAException.h>
#include <cuda.h>
#include <cuda_runtime.h>

__global__ void bias_swish_kernel(const float* __restrict__ in,
                                  const float* __restrict__ bias,
                                  float* __restrict__ out,
                                  long long total,
                                  int C,
                                  long long bias_numel,
                                  float inv_div) {
    long long idx = (long long)blockIdx.x * blockDim.x + threadIdx.x;
    if (idx >= total) return;

    float b;
    if (bias_numel == 1) {
        b = bias[0];
    } else if (bias_numel == (long long)C) {
        b = bias[idx % C];
    } else if (bias_numel == total) {
        b = bias[idx];
    } else {
        // Covers the only broadcast patterns used by this benchmark (scalar) and
        // common (C,) / full-size cases.  For unusual shapes, fall back to a
        // cyclic index rather than reading out of bounds.
        b = bias[idx % bias_numel];
    }

    float x = (in[idx] + b) * inv_div;
    float s = 1.0f / (1.0f + expf(-x));
    out[idx] = x * s;
}

torch::Tensor fused_forward(torch::Tensor input,
                           torch::Tensor weight,
                           torch::Tensor lin_bias,
                           torch::Tensor bn_weight,
                           torch::Tensor bn_bias,
                           torch::Tensor running_mean,
                           torch::Tensor running_var,
                           torch::Tensor extra_bias,
                           bool training,
                           double eps,
                           double momentum,
                           double divide_value) {
    // Keep the numerically sensitive / highly optimized GEMM and BatchNorm in
    // ATen/cuDNN, and fuse the remaining bias/divide/Swish chain into one CUDA
    // pass.  This removes several full-tensor PyTorch kernels from the forward.
    auto y = at::linear(input, weight, lin_bias);
    auto z = at::batch_norm(y, bn_weight, bn_bias, running_mean, running_var,
                            training, momentum, eps, true);

    auto out = torch::empty_like(z);
    const long long total = z.numel();
    if (total == 0) return out;

    const int C = (z.dim() >= 2) ? (int)z.size(1) : (int)total;
    const int threads = 256;
    const int blocks = (int)((total + threads - 1) / threads);
    float inv_div = 1.0f / static_cast<float>(divide_value);

    bias_swish_kernel<<<blocks, threads, 0, at::cuda::getCurrentCUDAStream()>>>(
        z.data_ptr<float>(), extra_bias.data_ptr<float>(), out.data_ptr<float>(),
        total, C, extra_bias.numel(), inv_div);
    C10_CUDA_KERNEL_LAUNCH_CHECK();
    return out;
}
'''

_ext = load_inline(
    name="bn_linear_swish_fused_ext_v1",
    cpp_sources=_cpp_src,
    cuda_sources=_cuda_src,
    functions=["fused_forward"],
    extra_cflags=["-O3"],
    extra_cuda_cflags=["-O3"],
    verbose=False,
)


class ModelNew(nn.Module):
    """
    Drop-in replacement for the reference module.  Parameters and buffers are
    named identically so state_dict copying works unchanged.
    """
    def __init__(self, in_features, out_features, bn_eps=1e-5, bn_momentum=0.1, bias_shape=(1,), divide_value=1.0):
        super(ModelNew, self).__init__()
        self.matmul = nn.Linear(in_features, out_features)
        self.bn = nn.BatchNorm1d(out_features, eps=bn_eps, momentum=bn_momentum)
        self.bias = nn.Parameter(torch.randn(bias_shape))
        self.divide_value = divide_value

    def forward(self, x):
        return _ext.fused_forward(
            x,
            self.matmul.weight,
            self.matmul.bias,
            self.bn.weight,
            self.bn.bias,
            self.bn.running_mean,
            self.bn.running_var,
            self.bias,
            self.bn.training,
            float(self.bn.eps),
            float(self.bn.momentum if self.bn.momentum is not None else 0.1),
            float(self.divide_value),
        )