correct
5_AlexNet.py
started
2026-05-05T00:02:00Z
timingkernelbench.timing
kernel mean / median / std
21.50 / — / 0.07 μs
ref mean / median / std
24.40 / — / 5.22 μs
numerical precisioneval_kernel_against_ref
gpu memoryextended_metrics.measure_memory
kernel launches / fusiontorch.profiler
fusion ratio (ref / kernel)
1.35x
energyNVML (pynvml)
ratio (ref / kernel)
1.33x
SOL (speed-of-light)heuristic_from_speedup
roofline / occupancyheuristic
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=1000):
"""
:param num_classes: The number of output classes (default is 1000 for ImageNet)
"""
super(Model, self).__init__()
# First convolutional layer
self.conv1 = nn.Conv2d(in_channels=3, out_channels=96, kernel_size=11, stride=4, padding=2)
self.relu1 = nn.ReLU(inplace=True)
self.maxpool1 = nn.MaxPool2d(kernel_size=3, stride=2)
# Second convolutional layer
self.conv2 = nn.Conv2d(in_channels=96, out_channels=256, kernel_size=5, padding=2)
self.relu2 = nn.ReLU(inplace=True)
self.maxpool2 = nn.MaxPool2d(kernel_size=3, stride=2)
# Third convolutional layer
self.conv3 = nn.Conv2d(in_channels=256, out_channels=384, kernel_size=3, padding=1)
self.relu3 = nn.ReLU(inplace=True)
# Fourth convolutional layer
self.conv4 = nn.Conv2d(in_channels=384, out_channels=384, kernel_size=3, padding=1)
self.relu4 = nn.ReLU(inplace=True)
# Fifth convolutional layer
self.conv5 = nn.Conv2d(in_channels=384, out_channels=256, kernel_size=3, padding=1)
self.relu5 = nn.ReLU(inplace=True)
self.maxpool3 = nn.MaxPool2d(kernel_size=3, stride=2)
# Fully connected layers
self.fc1 = nn.Linear(in_features=256 * 6 * 6, out_features=4096)
self.relu6 = nn.ReLU(inplace=True)
self.dropout1 = nn.Dropout(p=0.0)
self.fc2 = nn.Linear(in_features=4096, out_features=4096)
self.relu7 = nn.ReLU(inplace=True)
self.dropout2 = nn.Dropout(p=0.0)
self.fc3 = nn.Linear(in_features=4096, out_features=num_classes)
def forward(self, x):
"""
:param x: The input tensor, shape (batch_size, 3, 224, 224)
:return: The output tensor, shape (batch_size, num_classes)
"""
x = self.conv1(x)
x = self.relu1(x)
x = self.maxpool1(x)
x = self.conv2(x)
x = self.relu2(x)
x = self.maxpool2(x)
x = self.conv3(x)
x = self.relu3(x)
x = self.conv4(x)
x = self.relu4(x)
x = self.conv5(x)
x = self.relu5(x)
x = self.maxpool3(x)
x = torch.flatten(x, 1)
x = self.fc1(x)
x = self.relu6(x)
x = self.dropout1(x)
x = self.fc2(x)
x = self.relu7(x)
x = self.dropout2(x)
x = self.fc3(x)
return x
# Test code
batch_size = 1024
num_classes = 1000
def get_inputs():
return [torch.rand(batch_size, 3, 224, 224)]
def get_init_inputs():
return [num_classes]turn 0 · FINAL · 1 tool call(s) · 128.5s turn · 128.5s LLM · 8,637 chars (click to expand)
turn 0128.5s turn · 128.5s LLM · 0.0s tools · FINAL
reasoning
**Optimizing AlexNet Implementation**
I need to submit a complete Python implementation for optimizing AlexNet with a huge batch size of 1024. Should I implement the whole forward pass? I think I should use weights from the modules. If I'm creating a new model with the same constructor, will I need to copy the state_dict as well? I could subclass nn.Module to create the same layers. Maybe leveraging some of PyTorch's operations will help speed things up, but I'll need to ensure the output remains the same.
**Considering Custom Operations**
The requirement to "replace forward computation" doesn't necessarily mean all operations need to be custom. However, I wonder if doing so could be faster. PyTorch's conv and linear might be efficient, but the overhead from ReLU and pooling for a batch size of 1024 could add up. If I use `torch.compile`, it might not be allowed, so I’ll consider custom extensions. Implementing a fused ReLU-maxpool could save memory by avoiding a separate ReLU tensor. I’ll work out the dimensions for outputs in each layer too.
**Producing code confidently**
I'm considering binding a declared function, and I'm feeling good about how the linker will resolve it. I’ll set verbose to False and ensure the names are unique. One thing I want to watch out for is that load_inline with functions auto-generates PYBIND into the main cpp, and the cuda_sources definitions also include those function definitions. Overall, everything seems good, so let’s go ahead and produce the code!
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_cpp_src = r\"\"\"\n#include <torch/extension.h>\n\ntorch::Tensor relu_pool3x3s2_forward(torch::Tensor x);\ntorch::Tensor relu_inplace_forward(torch::Tensor x);\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\n__global__ void relu_pool3x3s2_kernel(const float* __restrict__ x,\n float* __restrict__ y,\n int total, int C, int H, int W,\n int outH, int outW) {\n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n if (idx >= total) return;\n\n int ow = idx % outW;\n int t = idx / outW;\n int oh = t % outH;\n t /= outH;\n int c = t % C;\n int n = t / C;\n\n int ih = oh * 2;\n int iw = ow * 2;\n int base = ((n * C + c) * H + ih) * W + iw;\n\n float m = x[base];\n float v;\n v = x[base + 1]; m = v > m ? v : m;\n v = x[base + 2]; m = v > m ? v : m;\n int b1 = base + W;\n v = x[b1]; m = v > m ? v : m;\n v = x[b1 + 1]; m = v > m ? v : m;\n v = x[b1 + 2]; m = v > m ? v : m;\n int b2 = base + 2 * W;\n v = x[b2]; m = v > m ? v : m;\n v = x[b2 + 1]; m = v > m ? v : m;\n v = x[b2 + 2]; m = v > m ? v : m;\n\n y[idx] = m > 0.0f ? m : 0.0f;\n}\n\n__global__ void relu_inplace_kernel(float* __restrict__ x, int total) {\n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n if (idx < total) {\n float v = x[idx];\n x[idx] = v > 0.0f ? v : 0.0f;\n }\n}\n\ntorch::Tensor relu_pool3x3s2_forward(torch::Tensor x) {\n TORCH_CHECK(x.is_cuda(), \"x must be CUDA\");\n TORCH_CHECK(x.scalar_type() == at::kFloat, \"only float32 supported\");\n TORCH_CHECK(x.dim() == 4, \"x must be NCHW\");\n TORCH_CHECK(x.is_contiguous(), \"x must be contiguous\");\n const int N = (int)x.size(0);\n const int C = (int)x.size(1);\n const int H = (int)x.size(2);\n const int W = (int)x.size(3);\n const int outH = (H - 3) / 2 + 1;\n const int outW = (W - 3) / 2 + 1;\n auto y = torch::empty({N, C, outH, outW}, x.options());\n const int total = N * C * outH * outW;\n const int threads = 256;\n const int blocks = (total + threads - 1) / threads;\n relu_pool3x3s2_kernel<<<blocks, threads, 0, at::cuda::getCurrentCUDAStream()>>>(\n x.data_ptr<float>(), y.data_ptr<float>(), total, C, H, W, outH, outW);\n return y;\n}\n\ntorch::Tensor relu_inplace_forward(torch::Tensor x) {\n TORCH_CHECK(x.is_cuda(), \"x must be CUDA\");\n TORCH_CHECK(x.scalar_type() == at::kFloat, \"only float32 supported\");\n TORCH_CHECK(x.is_contiguous(), \"x must be contiguous\");\n const int total = (int)x.numel();\n const int threads = 256;\n const int blocks = (total + threads - 1) / threads;\n relu_inplace_kernel<<<blocks, threads, 0, at::cuda::getCurrentCUDAStream()>>>(x.data_ptr<float>(), total);\n return x;\n}\n\"\"\"\n\n_ext = load_inline(\n name=\"alexnet_fused_relu_pool_ext_v1\",\n cpp_sources=_cpp_src,\n cuda_sources=_cuda_src,\n functions=[\"relu_pool3x3s2_forward\", \"relu_inplace_forward\"],\n extra_cflags=[\"-O3\"],\n extra_cuda_cflags=[\"-O3\"],\n verbose=False,\n)\n\n\nclass ModelNew(nn.Module):\n def __init__(self, num_classes=1000):\n super(ModelNew, self).__init__()\n self.conv1 = nn.Conv2d(in_channels=3, out_channels=96, kernel_size=11, stride=4, padding=2)\n self.relu1 = nn.ReLU(inplace=True)\n self.maxpool1 = nn.MaxPool2d(kernel_size=3, stride=2)\n self.conv2 = nn.Conv2d(in_channels=96, out_channels=256, kernel_size=5, padding=2)\n self.relu2 = nn.ReLU(inplace=True)\n self.maxpool2 = nn.MaxPool2d(kernel_size=3, stride=2)\n self.conv3 = nn.Conv2d(in_channels=256, out_channels=384, kernel_size=3, padding=1)\n self.relu3 = nn.ReLU(inplace=True)\n self.conv4 = nn.Conv2d(in_channels=384, out_channels=384, kernel_size=3, padding=1)\n self.relu4 = nn.ReLU(inplace=True)\n self.conv5 = nn.Conv2d(in_channels=384, out_channels=256, kernel_size=3, padding=1)\n self.relu5 = nn.ReLU(inplace=True)\n self.maxpool3 = nn.MaxPool2d(kernel_size=3, stride=2)\n self.fc1 = nn.Linear(in_features=256 * 6 * 6, out_features=4096)\n self.relu6 = nn.ReLU(inplace=True)\n self.dropout1 = nn.Dropout(p=0.0)\n self.fc2 = nn.Linear(in_features=4096, out_features=4096)\n self.relu7 = nn.ReLU(inplace=True)\n self.dropout2 = nn.Dropout(p=0.0)\n self.fc3 = nn.Linear(in_features=4096, out_features=num_classes)\n\n def forward(self, x):\n x = F.conv2d(x, self.conv1.weight, self.conv1.bias, stride=4, padding=2)\n x = _ext.relu_pool3x3s2_forward(x)\n\n x = F.conv2d(x, self.conv2.weight, self.conv2.bias, stride=1, padding=2)\n x = _ext.relu_pool3x3s2_forward(x)\n\n x = F.conv2d(x, self.conv3.weight, self.conv3.bias, stride=1, padding=1)\n x = _ext.relu_inplace_forward(x)\n\n x = F.conv2d(x, self.conv4.weight, self.conv4.bias, stride=1, padding=1)\n x = _ext.relu_inplace_forward(x)\n\n x = F.conv2d(x, self.conv5.weight, self.conv5.bias, stride=1, padding=1)\n x = _ext.relu_pool3x3s2_forward(x)\n\n x = torch.flatten(x, 1)\n x = F.linear(x, self.fc1.weight, self.fc1.bias)\n x = _ext.relu_inplace_forward(x)\n x = F.linear(x, self.fc2.weight, self.fc2.bias)\n x = _ext.relu_inplace_forward(x)\n x = F.linear(x, self.fc3.weight, self.fc3.bias)\n return x\n"
}
result ← submit_kernel (ok)
oksubmit_kernel
submit_kernel PASSED: (5 / 5) correctness trials all passed.
Kernel runtime: 21.50 μs
Runtime stats: mean=21.50μs median=?μs std=0.07μs
Numerical precision: max_abs_err=0.00e+00 mean_abs_err=0.00e+00 max_rel_err=0.00e+00
Memory: 3953.79 MB peak (ref: 3953.79 MB, ratio: 1.0x)
Kernel launches: 55 (ref: 74, fusion ratio: 1.3455)
Energy: 2900.78 mJ/run (ref: 3870.32 mJ, ratio: 1.3342x)
SOL score: 0.1135
final submitted kernel (151 lines, 5,604 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
_cpp_src = r"""
#include <torch/extension.h>
torch::Tensor relu_pool3x3s2_forward(torch::Tensor x);
torch::Tensor relu_inplace_forward(torch::Tensor x);
"""
_cuda_src = r"""
#include <torch/extension.h>
#include <ATen/cuda/CUDAContext.h>
#include <cuda.h>
#include <cuda_runtime.h>
__global__ void relu_pool3x3s2_kernel(const float* __restrict__ x,
float* __restrict__ y,
int total, int C, int H, int W,
int outH, int outW) {
int idx = blockIdx.x * blockDim.x + threadIdx.x;
if (idx >= total) return;
int ow = idx % outW;
int t = idx / outW;
int oh = t % outH;
t /= outH;
int c = t % C;
int n = t / C;
int ih = oh * 2;
int iw = ow * 2;
int base = ((n * C + c) * H + ih) * W + iw;
float m = x[base];
float v;
v = x[base + 1]; m = v > m ? v : m;
v = x[base + 2]; m = v > m ? v : m;
int b1 = base + W;
v = x[b1]; m = v > m ? v : m;
v = x[b1 + 1]; m = v > m ? v : m;
v = x[b1 + 2]; m = v > m ? v : m;
int b2 = base + 2 * W;
v = x[b2]; m = v > m ? v : m;
v = x[b2 + 1]; m = v > m ? v : m;
v = x[b2 + 2]; m = v > m ? v : m;
y[idx] = m > 0.0f ? m : 0.0f;
}
__global__ void relu_inplace_kernel(float* __restrict__ x, int total) {
int idx = blockIdx.x * blockDim.x + threadIdx.x;
if (idx < total) {
float v = x[idx];
x[idx] = v > 0.0f ? v : 0.0f;
}
}
torch::Tensor relu_pool3x3s2_forward(torch::Tensor x) {
TORCH_CHECK(x.is_cuda(), "x must be CUDA");
TORCH_CHECK(x.scalar_type() == at::kFloat, "only float32 supported");
TORCH_CHECK(x.dim() == 4, "x must be NCHW");
TORCH_CHECK(x.is_contiguous(), "x must be contiguous");
const int N = (int)x.size(0);
const int C = (int)x.size(1);
const int H = (int)x.size(2);
const int W = (int)x.size(3);
const int outH = (H - 3) / 2 + 1;
const int outW = (W - 3) / 2 + 1;
auto y = torch::empty({N, C, outH, outW}, x.options());
const int total = N * C * outH * outW;
const int threads = 256;
const int blocks = (total + threads - 1) / threads;
relu_pool3x3s2_kernel<<<blocks, threads, 0, at::cuda::getCurrentCUDAStream()>>>(
x.data_ptr<float>(), y.data_ptr<float>(), total, C, H, W, outH, outW);
return y;
}
torch::Tensor relu_inplace_forward(torch::Tensor x) {
TORCH_CHECK(x.is_cuda(), "x must be CUDA");
TORCH_CHECK(x.scalar_type() == at::kFloat, "only float32 supported");
TORCH_CHECK(x.is_contiguous(), "x must be contiguous");
const int total = (int)x.numel();
const int threads = 256;
const int blocks = (total + threads - 1) / threads;
relu_inplace_kernel<<<blocks, threads, 0, at::cuda::getCurrentCUDAStream()>>>(x.data_ptr<float>(), total);
return x;
}
"""
_ext = load_inline(
name="alexnet_fused_relu_pool_ext_v1",
cpp_sources=_cpp_src,
cuda_sources=_cuda_src,
functions=["relu_pool3x3s2_forward", "relu_inplace_forward"],
extra_cflags=["-O3"],
extra_cuda_cflags=["-O3"],
verbose=False,
)
class ModelNew(nn.Module):
def __init__(self, num_classes=1000):
super(ModelNew, self).__init__()
self.conv1 = nn.Conv2d(in_channels=3, out_channels=96, kernel_size=11, stride=4, padding=2)
self.relu1 = nn.ReLU(inplace=True)
self.maxpool1 = nn.MaxPool2d(kernel_size=3, stride=2)
self.conv2 = nn.Conv2d(in_channels=96, out_channels=256, kernel_size=5, padding=2)
self.relu2 = nn.ReLU(inplace=True)
self.maxpool2 = nn.MaxPool2d(kernel_size=3, stride=2)
self.conv3 = nn.Conv2d(in_channels=256, out_channels=384, kernel_size=3, padding=1)
self.relu3 = nn.ReLU(inplace=True)
self.conv4 = nn.Conv2d(in_channels=384, out_channels=384, kernel_size=3, padding=1)
self.relu4 = nn.ReLU(inplace=True)
self.conv5 = nn.Conv2d(in_channels=384, out_channels=256, kernel_size=3, padding=1)
self.relu5 = nn.ReLU(inplace=True)
self.maxpool3 = nn.MaxPool2d(kernel_size=3, stride=2)
self.fc1 = nn.Linear(in_features=256 * 6 * 6, out_features=4096)
self.relu6 = nn.ReLU(inplace=True)
self.dropout1 = nn.Dropout(p=0.0)
self.fc2 = nn.Linear(in_features=4096, out_features=4096)
self.relu7 = nn.ReLU(inplace=True)
self.dropout2 = nn.Dropout(p=0.0)
self.fc3 = nn.Linear(in_features=4096, out_features=num_classes)
def forward(self, x):
x = F.conv2d(x, self.conv1.weight, self.conv1.bias, stride=4, padding=2)
x = _ext.relu_pool3x3s2_forward(x)
x = F.conv2d(x, self.conv2.weight, self.conv2.bias, stride=1, padding=2)
x = _ext.relu_pool3x3s2_forward(x)
x = F.conv2d(x, self.conv3.weight, self.conv3.bias, stride=1, padding=1)
x = _ext.relu_inplace_forward(x)
x = F.conv2d(x, self.conv4.weight, self.conv4.bias, stride=1, padding=1)
x = _ext.relu_inplace_forward(x)
x = F.conv2d(x, self.conv5.weight, self.conv5.bias, stride=1, padding=1)
x = _ext.relu_pool3x3s2_forward(x)
x = torch.flatten(x, 1)
x = F.linear(x, self.fc1.weight, self.fc1.bias)
x = _ext.relu_inplace_forward(x)
x = F.linear(x, self.fc2.weight, self.fc2.bias)
x = _ext.relu_inplace_forward(x)
x = F.linear(x, self.fc3.weight, self.fc3.bias)
return x