gpt-5.5-priority · original · l123_single_gpt
correct 4_LeNet5.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
0.88 μs
ref runtime
0.96 μs
speedup
1.09x
started
2026-05-05T00:25:06Z
timingkernelbench.timing
kernel runtime
0.88 μs
ref runtime
0.96 μs
speedup
1.09x
kernel mean / median / std
0.88 / — / 0.46 μs
ref mean / median / std
0.96 / — / 1.34 μs
numerical precisioneval_kernel_against_ref
max abs error
7.45e-08
mean abs error
1.07e-08
max rel error
0.21
mean rel error
3.23e-06
gpu memoryextended_metrics.measure_memory
kernel peak
77.61 MB
ref peak
196.48 MB
kernel / ref
0.40x
kernel launches / fusiontorch.profiler
kernel launches
5
ref launches
42
fusion ratio (ref / kernel)
8.40x
total cuda time
41 μs
energyNVML (pynvml)
kernel mJ / run
242.64
ref mJ / run
1698.92
ratio (ref / kernel)
7.00x
avg power
292.87 W
ref avg power
2056.25 W
SOL (speed-of-light)nsight_hardware_counters
SOL score (0–1)
0.451
dram util %
14.9%
compute util %
45.0%
bottleneck
compute-bound
dominant pipe
FP32
arithmetic intensity
—
ridge point
20.00
roofline / occupancynsight_hardware_counters
dram bandwidth
498.95 GB/s
dram util %
14.9%
fp32 tflops
—
fp32 util %
—
fp16 tflops
—
occupancy %
358.9%
bottleneck
compute-bound
dominant pipe %
45.0%
arithmetic intensity
—
ridge point
20.00
L1 hit %
—
L2 hit %
371.8%
ld sectors / req
57.37
st sectors / req
—
regs / thread
119
smem / block
5120 B
block size
1280
peak bw
3350.00 GB/s
peak fp32 tflops
67.00
peak fp16 tflops
1979.00
warp stalls (top 5)nsight
long_scoreboard
9087.1%
mio_throttle
384.5%
short_scoreboard
202.6%
reference PyTorch source (the prompt the agent sees)
import torch
import torch.nn as nn
import torch.nn.functional as F

class Model(nn.Module):
    def __init__(self, num_classes):
        """
        LeNet-5 architecture implementation in PyTorch.

        :param num_classes: The number of output classes.
        """
        super(Model, self).__init__()
        
        # Convolutional layers
        self.conv1 = nn.Conv2d(in_channels=1, out_channels=6, kernel_size=5, stride=1)
        self.conv2 = nn.Conv2d(in_channels=6, out_channels=16, kernel_size=5, stride=1)
        
        # Fully connected layers
        self.fc1 = nn.Linear(in_features=16*5*5, out_features=120)
        self.fc2 = nn.Linear(in_features=120, out_features=84)
        self.fc3 = nn.Linear(in_features=84, out_features=num_classes)
    
    def forward(self, x):
        """
        Forward pass of the LeNet-5 model.

        :param x: The input tensor, shape (batch_size, 1, 32, 32)
        :return: The output tensor, shape (batch_size, num_classes)
        """
        # First convolutional layer with ReLU activation and max pooling
        x = F.relu(self.conv1(x))
        x = F.max_pool2d(x, kernel_size=2, stride=2)
        
        # Second convolutional layer with ReLU activation and max pooling
        x = F.relu(self.conv2(x))
        x = F.max_pool2d(x, kernel_size=2, stride=2)
        
        # Flatten the output for the fully connected layers
        x = x.view(-1, 16*5*5)
        
        # First fully connected layer with ReLU activation
        x = F.relu(self.fc1(x))
        
        # Second fully connected layer with ReLU activation
        x = F.relu(self.fc2(x))
        
        # Final fully connected layer
        x = self.fc3(x)
        
        return x

# Test code for the LeNet-5 model (larger batch & image)
batch_size = 4096
num_classes = 20

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

def get_init_inputs():
    return [num_classes]
turn 0 · FINAL · 1 tool call(s) · 39.7s turn · 39.7s LLM · 10,654 chars (click to expand)
turn 039.7s turn · 39.7s LLM · 0.0s tools · FINAL
reasoning
**Crafting CUDA code** I need to include the declaration in the cpp_sources and define it in cuda_sources using at::Tensor lenet_forward(...). I will return the tensor, but there's a potential issue with load_inline at compile time when using ninja, so the name should be unique. As for the kernel launch configuration, for conv1, the total is N*6*14*14, which is around 4.8 million parameters, with a block size of 256. The totals for conv2, fc1, fc2, and fc3 need careful consideration, including intermediate calculations and the application of ReLU. **Indexing convolution layers** I'm figuring out the indexing for the convolution layers. For Conv1, the input shape is [N, 1, 32, 32], and the convolution output should be 28x28. After pooling, the output size becomes 14x14. I need to calculate the pooling positions at oy and ox, using the formula iy = 2*oy + py (where py ranges from 0 to 27), as well as ix for the indexing. This setup is important for ensuring the correct flow through the layers.
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\nat::Tensor lenet_forward_cuda(at::Tensor x,\n at::Tensor c1w, at::Tensor c1b,\n at::Tensor c2w, at::Tensor c2b,\n at::Tensor f1w, at::Tensor f1b,\n at::Tensor f2w, at::Tensor f2b,\n at::Tensor f3w, at::Tensor f3b);\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 <float.h>\n\n__global__ void conv1_pool_kernel(const float* __restrict__ x,\n const float* __restrict__ w,\n const float* __restrict__ b,\n float* __restrict__ y,\n int N) {\n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n int total = N * 6 * 14 * 14;\n if (idx >= total) return;\n\n int t = idx;\n int ox = t % 14; t /= 14;\n int oy = t % 14; t /= 14;\n int oc = t % 6; t /= 6;\n int n = t;\n\n const float* xp = x + n * 32 * 32;\n const float* wp = w + oc * 25;\n float best = -FLT_MAX;\n\n #pragma unroll\n for (int py = 0; py < 2; ++py) {\n #pragma unroll\n for (int px = 0; px < 2; ++px) {\n int iy = oy * 2 + py;\n int ix = ox * 2 + px;\n float s = b[oc];\n #pragma unroll\n for (int ky = 0; ky < 5; ++ky) {\n #pragma unroll\n for (int kx = 0; kx < 5; ++kx) {\n s = fmaf(xp[(iy + ky) * 32 + (ix + kx)], wp[ky * 5 + kx], s);\n }\n }\n best = fmaxf(best, s);\n }\n }\n y[idx] = fmaxf(best, 0.0f);\n}\n\n__global__ void conv2_pool_kernel(const float* __restrict__ x,\n const float* __restrict__ w,\n const float* __restrict__ b,\n float* __restrict__ y,\n int N) {\n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n int total = N * 16 * 5 * 5;\n if (idx >= total) return;\n\n int t = idx;\n int ox = t % 5; t /= 5;\n int oy = t % 5; t /= 5;\n int oc = t % 16; t /= 16;\n int n = t;\n\n const float* xp = x + n * (6 * 14 * 14);\n const float* wp0 = w + oc * (6 * 25);\n float best = -FLT_MAX;\n\n #pragma unroll\n for (int py = 0; py < 2; ++py) {\n #pragma unroll\n for (int px = 0; px < 2; ++px) {\n int iy = oy * 2 + py;\n int ix = ox * 2 + px;\n float s = b[oc];\n #pragma unroll\n for (int ic = 0; ic < 6; ++ic) {\n const float* xpc = xp + ic * 14 * 14;\n const float* wpc = wp0 + ic * 25;\n #pragma unroll\n for (int ky = 0; ky < 5; ++ky) {\n #pragma unroll\n for (int kx = 0; kx < 5; ++kx) {\n s = fmaf(xpc[(iy + ky) * 14 + (ix + kx)], wpc[ky * 5 + kx], s);\n }\n }\n }\n best = fmaxf(best, s);\n }\n }\n y[idx] = fmaxf(best, 0.0f);\n}\n\n__global__ void fc1_kernel(const float* __restrict__ x,\n const float* __restrict__ w,\n const float* __restrict__ b,\n float* __restrict__ y,\n int N) {\n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n int total = N * 120;\n if (idx >= total) return;\n int o = idx % 120;\n int n = idx / 120;\n const float* xp = x + n * 400;\n const float* wp = w + o * 400;\n float s = b[o];\n #pragma unroll 4\n for (int k = 0; k < 400; ++k) {\n s = fmaf(xp[k], wp[k], s);\n }\n y[idx] = fmaxf(s, 0.0f);\n}\n\n__global__ void fc2_kernel(const float* __restrict__ x,\n const float* __restrict__ w,\n const float* __restrict__ b,\n float* __restrict__ y,\n int N) {\n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n int total = N * 84;\n if (idx >= total) return;\n int o = idx % 84;\n int n = idx / 84;\n const float* xp = x + n * 120;\n const float* wp = w + o * 120;\n float s = b[o];\n #pragma unroll 4\n for (int k = 0; k < 120; ++k) {\n s = fmaf(xp[k], wp[k], s);\n }\n y[idx] = fmaxf(s, 0.0f);\n}\n\n__global__ void fc3_kernel(const float* __restrict__ x,\n const float* __restrict__ w,\n const float* __restrict__ b,\n float* __restrict__ y,\n int N, int C) {\n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n int total = N * C;\n if (idx >= total) return;\n int o = idx % C;\n int n = idx / C;\n const float* xp = x + n * 84;\n const float* wp = w + o * 84;\n float s = b[o];\n #pragma unroll 4\n for (int k = 0; k < 84; ++k) {\n s = fmaf(xp[k], wp[k], s);\n }\n y[idx] = s;\n}\n\nat::Tensor lenet_forward_cuda(at::Tensor x,\n at::Tensor c1w, at::Tensor c1b,\n at::Tensor c2w, at::Tensor c2b,\n at::Tensor f1w, at::Tensor f1b,\n at::Tensor f2w, at::Tensor f2b,\n at::Tensor f3w, at::Tensor f3b) {\n const int N = (int)x.size(0);\n const int C = (int)f3b.size(0);\n auto opts = x.options();\n at::Tensor y1 = at::empty({N, 6, 14, 14}, opts);\n at::Tensor y2 = at::empty({N, 16, 5, 5}, opts);\n at::Tensor y3 = at::empty({N, 120}, opts);\n at::Tensor y4 = at::empty({N, 84}, opts);\n at::Tensor out = at::empty({N, C}, opts);\n\n cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n const int block = 256;\n int total1 = N * 6 * 14 * 14;\n conv1_pool_kernel<<<(total1 + block - 1) / block, block, 0, stream>>>(\n x.data_ptr<float>(), c1w.data_ptr<float>(), c1b.data_ptr<float>(), y1.data_ptr<float>(), N);\n int total2 = N * 16 * 5 * 5;\n conv2_pool_kernel<<<(total2 + block - 1) / block, block, 0, stream>>>(\n y1.data_ptr<float>(), c2w.data_ptr<float>(), c2b.data_ptr<float>(), y2.data_ptr<float>(), N);\n int total3 = N * 120;\n fc1_kernel<<<(total3 + block - 1) / block, block, 0, stream>>>(\n y2.data_ptr<float>(), f1w.data_ptr<float>(), f1b.data_ptr<float>(), y3.data_ptr<float>(), N);\n int total4 = N * 84;\n fc2_kernel<<<(total4 + block - 1) / block, block, 0, stream>>>(\n y3.data_ptr<float>(), f2w.data_ptr<float>(), f2b.data_ptr<float>(), y4.data_ptr<float>(), N);\n int total5 = N * C;\n fc3_kernel<<<(total5 + block - 1) / block, block, 0, stream>>>(\n y4.data_ptr<float>(), f3w.data_ptr<float>(), f3b.data_ptr<float>(), out.data_ptr<float>(), N, C);\n return out;\n}\n\"\"\"\n\n_ext = load_inline(\n name=\"lenet5_fused_forward_ext\",\n cpp_sources=_cpp_src,\n cuda_sources=_cuda_src,\n functions=[\"lenet_forward_cuda\"],\n extra_cflags=[\"-O3\"],\n extra_cuda_cflags=[\"-O3\", \"--use_fast_math\"],\n verbose=False,\n)\n\nclass ModelNew(nn.Module):\n def __init__(self, num_classes):\n super(ModelNew, self).__init__()\n self.conv1 = nn.Conv2d(in_channels=1, out_channels=6, kernel_size=5, stride=1)\n self.conv2 = nn.Conv2d(in_channels=6, out_channels=16, kernel_size=5, stride=1)\n self.fc1 = nn.Linear(in_features=16*5*5, out_features=120)\n self.fc2 = nn.Linear(in_features=120, out_features=84)\n self.fc3 = nn.Linear(in_features=84, out_features=num_classes)\n\n def forward(self, x):\n return _ext.lenet_forward_cuda(\n x,\n self.conv1.weight, self.conv1.bias,\n self.conv2.weight, self.conv2.bias,\n self.fc1.weight, self.fc1.bias,\n self.fc2.weight, self.fc2.bias,\n self.fc3.weight, self.fc3.bias,\n )\n" }
result ← submit_kernel (ok)
oksubmit_kernel submit_kernel PASSED: (5 / 5) correctness trials all passed. Kernel runtime: 0.88 μs Runtime stats: mean=0.88μs median=?μs std=0.46μs Numerical precision: max_abs_err=7.45e-08 mean_abs_err=1.07e-08 max_rel_err=2.12e-01 Memory: 77.61 MB peak (ref: 196.48 MB, ratio: 0.395x) Kernel launches: 5 (ref: 42, fusion ratio: 8.4) Energy: 242.64 mJ/run (ref: 1698.92 mJ, ratio: 7.0018x) SOL score: 0.4505
final submitted kernel (230 lines, 7,997 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>

at::Tensor lenet_forward_cuda(at::Tensor x,
                              at::Tensor c1w, at::Tensor c1b,
                              at::Tensor c2w, at::Tensor c2b,
                              at::Tensor f1w, at::Tensor f1b,
                              at::Tensor f2w, at::Tensor f2b,
                              at::Tensor f3w, at::Tensor f3b);
"""

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

__global__ void conv1_pool_kernel(const float* __restrict__ x,
                                  const float* __restrict__ w,
                                  const float* __restrict__ b,
                                  float* __restrict__ y,
                                  int N) {
    int idx = blockIdx.x * blockDim.x + threadIdx.x;
    int total = N * 6 * 14 * 14;
    if (idx >= total) return;

    int t = idx;
    int ox = t % 14; t /= 14;
    int oy = t % 14; t /= 14;
    int oc = t % 6;  t /= 6;
    int n = t;

    const float* xp = x + n * 32 * 32;
    const float* wp = w + oc * 25;
    float best = -FLT_MAX;

    #pragma unroll
    for (int py = 0; py < 2; ++py) {
        #pragma unroll
        for (int px = 0; px < 2; ++px) {
            int iy = oy * 2 + py;
            int ix = ox * 2 + px;
            float s = b[oc];
            #pragma unroll
            for (int ky = 0; ky < 5; ++ky) {
                #pragma unroll
                for (int kx = 0; kx < 5; ++kx) {
                    s = fmaf(xp[(iy + ky) * 32 + (ix + kx)], wp[ky * 5 + kx], s);
                }
            }
            best = fmaxf(best, s);
        }
    }
    y[idx] = fmaxf(best, 0.0f);
}

__global__ void conv2_pool_kernel(const float* __restrict__ x,
                                  const float* __restrict__ w,
                                  const float* __restrict__ b,
                                  float* __restrict__ y,
                                  int N) {
    int idx = blockIdx.x * blockDim.x + threadIdx.x;
    int total = N * 16 * 5 * 5;
    if (idx >= total) return;

    int t = idx;
    int ox = t % 5; t /= 5;
    int oy = t % 5; t /= 5;
    int oc = t % 16; t /= 16;
    int n = t;

    const float* xp = x + n * (6 * 14 * 14);
    const float* wp0 = w + oc * (6 * 25);
    float best = -FLT_MAX;

    #pragma unroll
    for (int py = 0; py < 2; ++py) {
        #pragma unroll
        for (int px = 0; px < 2; ++px) {
            int iy = oy * 2 + py;
            int ix = ox * 2 + px;
            float s = b[oc];
            #pragma unroll
            for (int ic = 0; ic < 6; ++ic) {
                const float* xpc = xp + ic * 14 * 14;
                const float* wpc = wp0 + ic * 25;
                #pragma unroll
                for (int ky = 0; ky < 5; ++ky) {
                    #pragma unroll
                    for (int kx = 0; kx < 5; ++kx) {
                        s = fmaf(xpc[(iy + ky) * 14 + (ix + kx)], wpc[ky * 5 + kx], s);
                    }
                }
            }
            best = fmaxf(best, s);
        }
    }
    y[idx] = fmaxf(best, 0.0f);
}

__global__ void fc1_kernel(const float* __restrict__ x,
                           const float* __restrict__ w,
                           const float* __restrict__ b,
                           float* __restrict__ y,
                           int N) {
    int idx = blockIdx.x * blockDim.x + threadIdx.x;
    int total = N * 120;
    if (idx >= total) return;
    int o = idx % 120;
    int n = idx / 120;
    const float* xp = x + n * 400;
    const float* wp = w + o * 400;
    float s = b[o];
    #pragma unroll 4
    for (int k = 0; k < 400; ++k) {
        s = fmaf(xp[k], wp[k], s);
    }
    y[idx] = fmaxf(s, 0.0f);
}

__global__ void fc2_kernel(const float* __restrict__ x,
                           const float* __restrict__ w,
                           const float* __restrict__ b,
                           float* __restrict__ y,
                           int N) {
    int idx = blockIdx.x * blockDim.x + threadIdx.x;
    int total = N * 84;
    if (idx >= total) return;
    int o = idx % 84;
    int n = idx / 84;
    const float* xp = x + n * 120;
    const float* wp = w + o * 120;
    float s = b[o];
    #pragma unroll 4
    for (int k = 0; k < 120; ++k) {
        s = fmaf(xp[k], wp[k], s);
    }
    y[idx] = fmaxf(s, 0.0f);
}

__global__ void fc3_kernel(const float* __restrict__ x,
                           const float* __restrict__ w,
                           const float* __restrict__ b,
                           float* __restrict__ y,
                           int N, int C) {
    int idx = blockIdx.x * blockDim.x + threadIdx.x;
    int total = N * C;
    if (idx >= total) return;
    int o = idx % C;
    int n = idx / C;
    const float* xp = x + n * 84;
    const float* wp = w + o * 84;
    float s = b[o];
    #pragma unroll 4
    for (int k = 0; k < 84; ++k) {
        s = fmaf(xp[k], wp[k], s);
    }
    y[idx] = s;
}

at::Tensor lenet_forward_cuda(at::Tensor x,
                              at::Tensor c1w, at::Tensor c1b,
                              at::Tensor c2w, at::Tensor c2b,
                              at::Tensor f1w, at::Tensor f1b,
                              at::Tensor f2w, at::Tensor f2b,
                              at::Tensor f3w, at::Tensor f3b) {
    const int N = (int)x.size(0);
    const int C = (int)f3b.size(0);
    auto opts = x.options();
    at::Tensor y1 = at::empty({N, 6, 14, 14}, opts);
    at::Tensor y2 = at::empty({N, 16, 5, 5}, opts);
    at::Tensor y3 = at::empty({N, 120}, opts);
    at::Tensor y4 = at::empty({N, 84}, opts);
    at::Tensor out = at::empty({N, C}, opts);

    cudaStream_t stream = at::cuda::getCurrentCUDAStream();
    const int block = 256;
    int total1 = N * 6 * 14 * 14;
    conv1_pool_kernel<<<(total1 + block - 1) / block, block, 0, stream>>>(
        x.data_ptr<float>(), c1w.data_ptr<float>(), c1b.data_ptr<float>(), y1.data_ptr<float>(), N);
    int total2 = N * 16 * 5 * 5;
    conv2_pool_kernel<<<(total2 + block - 1) / block, block, 0, stream>>>(
        y1.data_ptr<float>(), c2w.data_ptr<float>(), c2b.data_ptr<float>(), y2.data_ptr<float>(), N);
    int total3 = N * 120;
    fc1_kernel<<<(total3 + block - 1) / block, block, 0, stream>>>(
        y2.data_ptr<float>(), f1w.data_ptr<float>(), f1b.data_ptr<float>(), y3.data_ptr<float>(), N);
    int total4 = N * 84;
    fc2_kernel<<<(total4 + block - 1) / block, block, 0, stream>>>(
        y3.data_ptr<float>(), f2w.data_ptr<float>(), f2b.data_ptr<float>(), y4.data_ptr<float>(), N);
    int total5 = N * C;
    fc3_kernel<<<(total5 + block - 1) / block, block, 0, stream>>>(
        y4.data_ptr<float>(), f3w.data_ptr<float>(), f3b.data_ptr<float>(), out.data_ptr<float>(), N, C);
    return out;
}
"""

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

class ModelNew(nn.Module):
    def __init__(self, num_classes):
        super(ModelNew, self).__init__()
        self.conv1 = nn.Conv2d(in_channels=1, out_channels=6, kernel_size=5, stride=1)
        self.conv2 = nn.Conv2d(in_channels=6, out_channels=16, kernel_size=5, stride=1)
        self.fc1 = nn.Linear(in_features=16*5*5, out_features=120)
        self.fc2 = nn.Linear(in_features=120, out_features=84)
        self.fc3 = nn.Linear(in_features=84, out_features=num_classes)

    def forward(self, x):
        return _ext.lenet_forward_cuda(
            x,
            self.conv1.weight, self.conv1.bias,
            self.conv2.weight, self.conv2.bias,
            self.fc1.weight, self.fc1.bias,
            self.fc2.weight, self.fc2.bias,
            self.fc3.weight, self.fc3.bias,
        )