gpt-5.5-priority · original · l3_default_gpt
correct 4_LeNet5.py
model
gpt-5.5-priority
turns / max
9 / 15
tool calls
9
tokens (total)
0
wall clock
—
truncated
no
backend
cuda fp32
variant
original
runtime
0.86 μs
ref runtime
0.75 μs
speedup
0.87x
started
2026-05-04T20:53:31Z
timingkernelbench.timing
kernel runtime
0.86 μs
ref runtime
0.75 μs
speedup
0.87x
kernel mean / median / std
0.86 / — / 0.04 μs
ref mean / median / std
0.75 / — / 1.04 μs
numerical precisioneval_kernel_against_ref
max abs error
7.64e-08
mean abs error
1.11e-08
max rel error
0.21
mean rel error
3.63e-06
gpu memoryextended_metrics.measure_memory
kernel peak
49.79 MB
ref peak
326.98 MB
kernel / ref
0.15x
kernel launches / fusiontorch.profiler
kernel launches
1
ref launches
42
fusion ratio (ref / kernel)
42.00x
total cuda time
22 μs
SOL (speed-of-light)nsight_hardware_counters
SOL score (0–1)
0.067
dram util %
0.6%
compute util %
6.7%
bottleneck
compute-bound
dominant pipe
FP32
arithmetic intensity
—
ridge point
20.00
roofline / occupancynsight_hardware_counters
dram bandwidth
19.88 GB/s
dram util %
0.6%
fp32 tflops
—
fp32 util %
—
fp16 tflops
—
occupancy %
23.6%
bottleneck
compute-bound
dominant pipe %
6.7%
arithmetic intensity
—
ridge point
20.00
L1 hit %
—
L2 hit %
94.6%
ld sectors / req
21.05
st sectors / req
—
regs / thread
96
smem / block
18432 B
block size
256
peak bw
3350.00 GB/s
peak fp32 tflops
67.00
peak fp16 tflops
1979.00
warp stalls (top 5)nsight
long_scoreboard
978.4%
mio_throttle
286.7%
short_scoreboard
101.1%
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 019.1s turn · 19.1s LLM · 0.0s tools
call → get_gpu_specs
callget_gpu_specs {}
result ← get_gpu_specs (ok)
okget_gpu_specs GPU specs for NVIDIA H100 80GB HBM3: total memory (runtime): 79.1 GB spec entry: H100 GPU Architecture: Hopper GPU Memory: 80GB Memory Bandwidth: 3.35 TB/s FP64 TFLOPS: 34 FP64 Tensor Core TFLOPS: 67 FP32 TFLOPS: 67 TF32 Tensor Core TFLOPS: 989 with sparsity BFLOAT16 Tensore Core TFLOPS: 1979 with sparsity FP16 Tensor Core TFLOPS: 1979 with sparsity FP8 Tensor Core TFLOPS: 3958 with sparsity INT8 Tensor Core TOPS: 3958 with sparsity Register File Size: 64K 32-bit registers per SM Maximum number of registers per thread: 255 Maximum number of thread blocks per SM: 32 Shared memory capacity per SM: 228 KB Maximum shared memory per thread block: 227 KB
turn 1 · 1 tool call(s) · 72.2s turn · 72.2s LLM · 7,558 chars (click to expand)
turn 172.2s turn · 72.2s LLM · 0.0s tools
call → compile_kernel
callcompile_kernel { "kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\n_src_cpp = r\"\"\"\n#include <torch/extension.h>\n\ntorch::Tensor lenet_forward(torch::Tensor x,\n torch::Tensor c1w, torch::Tensor c1b,\n torch::Tensor c2w, torch::Tensor c2b,\n torch::Tensor f1w, torch::Tensor f1b,\n torch::Tensor f2w, torch::Tensor f2b,\n torch::Tensor f3w, torch::Tensor f3b);\n\"\"\"\n\n_src_cuda = 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 lenet_fused_kernel(const float* __restrict__ x,\n const float* __restrict__ c1w, const float* __restrict__ c1b,\n const float* __restrict__ c2w, const float* __restrict__ c2b,\n const float* __restrict__ f1w, const float* __restrict__ f1b,\n const float* __restrict__ f2w, const float* __restrict__ f2b,\n const float* __restrict__ f3w, const float* __restrict__ f3b,\n float* __restrict__ out,\n int N, int K) {\n int n = blockIdx.x;\n int tid = threadIdx.x;\n __shared__ float p1[6*14*14];\n __shared__ float p2[16*5*5];\n __shared__ float h1[120];\n __shared__ float h2[84];\n\n const float* xin = x + ((size_t)n)*32*32;\n\n // conv1 + relu + 2x2 maxpool: output p1[6,14,14]\n for (int idx = tid; idx < 6*14*14; idx += blockDim.x) {\n int tmp = idx;\n int px = tmp % 14; tmp /= 14;\n int py = tmp % 14; tmp /= 14;\n int oc = tmp;\n float m = 0.0f;\n #pragma unroll\n for (int dy = 0; dy < 2; ++dy) {\n #pragma unroll\n for (int dx = 0; dx < 2; ++dx) {\n int oy = py*2 + dy;\n int ox = px*2 + dx;\n float s = c1b[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 += c1w[oc*25 + ky*5 + kx] * xin[(oy+ky)*32 + (ox+kx)];\n }\n }\n if (s > m) m = s;\n }\n }\n p1[idx] = m;\n }\n __syncthreads();\n\n // conv2 + relu + 2x2 maxpool: output p2[16,5,5]\n for (int idx = tid; idx < 16*5*5; idx += blockDim.x) {\n int tmp = idx;\n int px = tmp % 5; tmp /= 5;\n int py = tmp % 5; tmp /= 5;\n int oc = tmp;\n float m = 0.0f;\n #pragma unroll\n for (int dy = 0; dy < 2; ++dy) {\n #pragma unroll\n for (int dx = 0; dx < 2; ++dx) {\n int oy = py*2 + dy;\n int ox = px*2 + dx;\n float s = c2b[oc];\n #pragma unroll\n for (int ic = 0; ic < 6; ++ic) {\n #pragma unroll\n for (int ky = 0; ky < 5; ++ky) {\n #pragma unroll\n for (int kx = 0; kx < 5; ++kx) {\n s += c2w[((oc*6 + ic)*5 + ky)*5 + kx] * p1[(ic*14 + (oy+ky))*14 + (ox+kx)];\n }\n }\n }\n if (s > m) m = s;\n }\n }\n p2[idx] = m;\n }\n __syncthreads();\n\n // fc1 + relu\n for (int o = tid; o < 120; o += blockDim.x) {\n float s = f1b[o];\n const float* w = f1w + o*400;\n #pragma unroll 4\n for (int i = 0; i < 400; ++i) {\n s += w[i] * p2[i];\n }\n h1[o] = s > 0.0f ? s : 0.0f;\n }\n __syncthreads();\n\n // fc2 + relu\n for (int o = tid; o < 84; o += blockDim.x) {\n float s = f2b[o];\n const float* w = f2w + o*120;\n #pragma unroll 4\n for (int i = 0; i < 120; ++i) {\n s += w[i] * h1[i];\n }\n h2[o] = s > 0.0f ? s : 0.0f;\n }\n __syncthreads();\n\n // fc3 (K normally 20)\n for (int o = tid; o < K; o += blockDim.x) {\n float s = f3b[o];\n const float* w = f3w + ((size_t)o)*84;\n #pragma unroll\n for (int i = 0; i < 84; ++i) {\n s += w[i] * h2[i];\n }\n out[((size_t)n)*K + o] = s;\n }\n}\n\ntorch::Tensor lenet_forward(torch::Tensor x,\n torch::Tensor c1w, torch::Tensor c1b,\n torch::Tensor c2w, torch::Tensor c2b,\n torch::Tensor f1w, torch::Tensor f1b,\n torch::Tensor f2w, torch::Tensor f2b,\n torch::Tensor f3w, torch::Tensor f3b) {\n const int N = (int)x.size(0);\n const int K = (int)f3b.size(0);\n auto out = torch::empty({N, K}, x.options());\n dim3 grid(N);\n dim3 block(256);\n cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n lenet_fused_kernel<<<grid, block, 0, stream>>>(\n x.data_ptr<float>(), c1w.data_ptr<float>(), c1b.data_ptr<float>(),\n c2w.data_ptr<float>(), c2b.data_ptr<float>(),\n f1w.data_ptr<float>(), f1b.data_ptr<float>(),\n f2w.data_ptr<float>(), f2b.data_ptr<float>(),\n f3w.data_ptr<float>(), f3b.data_ptr<float>(),\n out.data_ptr<float>(), N, K);\n return out;\n}\n\"\"\"\n\n_ext = load_inline(name=\"lenet5_fused_ext_v1\",\n cpp_sources=_src_cpp,\n cuda_sources=_src_cuda,\n functions=[\"lenet_forward\"],\n extra_cuda_cflags=[\"-O3\", \"--use_fast_math\"],\n extra_cflags=[\"-O3\"],\n verbose=False)\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 def forward(self, x):\n return _ext.lenet_forward(x, 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" }
result ← compile_kernel (ok)
okcompile_kernel compile_kernel PASSED: kernel compiled without errors.
turn 2 · 1 tool call(s) · 18.1s turn · 18.1s LLM · 7,660 chars (click to expand)
turn 218.1s turn · 18.1s LLM · 0.0s tools
call → run_correctness
callrun_correctness { "kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\n_src_cpp = r\"\"\"\n#include <torch/extension.h>\n\ntorch::Tensor lenet_forward(torch::Tensor x,\n torch::Tensor c1w, torch::Tensor c1b,\n torch::Tensor c2w, torch::Tensor c2b,\n torch::Tensor f1w, torch::Tensor f1b,\n torch::Tensor f2w, torch::Tensor f2b,\n torch::Tensor f3w, torch::Tensor f3b);\n\"\"\"\n\n_src_cuda = 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 lenet_fused_kernel(const float* __restrict__ x,\n const float* __restrict__ c1w, const float* __restrict__ c1b,\n const float* __restrict__ c2w, const float* __restrict__ c2b,\n const float* __restrict__ f1w, const float* __restrict__ f1b,\n const float* __restrict__ f2w, const float* __restrict__ f2b,\n const float* __restrict__ f3w, const float* __restrict__ f3b,\n float* __restrict__ out,\n int N, int K) {\n int n = blockIdx.x;\n int tid = threadIdx.x;\n __shared__ float p1[6*14*14];\n __shared__ float p2[16*5*5];\n __shared__ float h1[120];\n __shared__ float h2[84];\n\n const float* xin = x + ((size_t)n)*32*32;\n\n // conv1 + relu + 2x2 maxpool: output p1[6,14,14]\n for (int idx = tid; idx < 6*14*14; idx += blockDim.x) {\n int tmp = idx;\n int px = tmp % 14; tmp /= 14;\n int py = tmp % 14; tmp /= 14;\n int oc = tmp;\n float m = 0.0f;\n #pragma unroll\n for (int dy = 0; dy < 2; ++dy) {\n #pragma unroll\n for (int dx = 0; dx < 2; ++dx) {\n int oy = py*2 + dy;\n int ox = px*2 + dx;\n float s = c1b[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 += c1w[oc*25 + ky*5 + kx] * xin[(oy+ky)*32 + (ox+kx)];\n }\n }\n if (s > m) m = s;\n }\n }\n p1[idx] = m;\n }\n __syncthreads();\n\n // conv2 + relu + 2x2 maxpool: output p2[16,5,5]\n for (int idx = tid; idx < 16*5*5; idx += blockDim.x) {\n int tmp = idx;\n int px = tmp % 5; tmp /= 5;\n int py = tmp % 5; tmp /= 5;\n int oc = tmp;\n float m = 0.0f;\n #pragma unroll\n for (int dy = 0; dy < 2; ++dy) {\n #pragma unroll\n for (int dx = 0; dx < 2; ++dx) {\n int oy = py*2 + dy;\n int ox = px*2 + dx;\n float s = c2b[oc];\n #pragma unroll\n for (int ic = 0; ic < 6; ++ic) {\n #pragma unroll\n for (int ky = 0; ky < 5; ++ky) {\n #pragma unroll\n for (int kx = 0; kx < 5; ++kx) {\n s += c2w[((oc*6 + ic)*5 + ky)*5 + kx] * p1[(ic*14 + (oy+ky))*14 + (ox+kx)];\n }\n }\n }\n if (s > m) m = s;\n }\n }\n p2[idx] = m;\n }\n __syncthreads();\n\n // fc1 + relu\n for (int o = tid; o < 120; o += blockDim.x) {\n float s = f1b[o];\n const float* w = f1w + o*400;\n #pragma unroll 4\n for (int i = 0; i < 400; ++i) {\n s += w[i] * p2[i];\n }\n h1[o] = s > 0.0f ? s : 0.0f;\n }\n __syncthreads();\n\n // fc2 + relu\n for (int o = tid; o < 84; o += blockDim.x) {\n float s = f2b[o];\n const float* w = f2w + o*120;\n #pragma unroll 4\n for (int i = 0; i < 120; ++i) {\n s += w[i] * h1[i];\n }\n h2[o] = s > 0.0f ? s : 0.0f;\n }\n __syncthreads();\n\n // fc3 (K normally 20)\n for (int o = tid; o < K; o += blockDim.x) {\n float s = f3b[o];\n const float* w = f3w + ((size_t)o)*84;\n #pragma unroll\n for (int i = 0; i < 84; ++i) {\n s += w[i] * h2[i];\n }\n out[((size_t)n)*K + o] = s;\n }\n}\n\ntorch::Tensor lenet_forward(torch::Tensor x,\n torch::Tensor c1w, torch::Tensor c1b,\n torch::Tensor c2w, torch::Tensor c2b,\n torch::Tensor f1w, torch::Tensor f1b,\n torch::Tensor f2w, torch::Tensor f2b,\n torch::Tensor f3w, torch::Tensor f3b) {\n const int N = (int)x.size(0);\n const int K = (int)f3b.size(0);\n auto out = torch::empty({N, K}, x.options());\n dim3 grid(N);\n dim3 block(256);\n cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n lenet_fused_kernel<<<grid, block, 0, stream>>>(\n x.data_ptr<float>(), c1w.data_ptr<float>(), c1b.data_ptr<float>(),\n c2w.data_ptr<float>(), c2b.data_ptr<float>(),\n f1w.data_ptr<float>(), f1b.data_ptr<float>(),\n f2w.data_ptr<float>(), f2b.data_ptr<float>(),\n f3w.data_ptr<float>(), f3b.data_ptr<float>(),\n out.data_ptr<float>(), N, K);\n return out;\n}\n\"\"\"\n\n_ext = load_inline(name=\"lenet5_fused_ext_v1\",\n cpp_sources=_src_cpp,\n cuda_sources=_src_cuda,\n functions=[\"lenet_forward\"],\n extra_cuda_cflags=[\"-O3\", \"--use_fast_math\"],\n extra_cflags=[\"-O3\"],\n verbose=False)\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 def forward(self, x):\n return _ext.lenet_forward(x, 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" }
result ← run_correctness (ok)
okrun_correctness run_correctness PASSED: (5 / 5) trials all matched the reference. Numerical precision: max_abs_err=7.64e-08 mean_abs_err=1.11e-08 max_rel_err=2.12e-01
turn 3 · 1 tool call(s) · 35.7s turn · 35.7s LLM · 7,813 chars (click to expand)
turn 335.7s turn · 35.7s LLM · 0.0s tools
reasoning
**Evaluating performance metrics** I'm considering achieving a few TFLOPs with a response time of about 0.5 milliseconds. It seems like PyTorch could have a similar performance, especially when I factor in any overhead that might be present. This level of performance is intriguing, and I'll have to think more about how to optimize for both speed and efficiency. Ultimately, it’s about finding the right balance to meet user needs effectively!
call → compile_kernel
callcompile_kernel { "kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\n_src_cpp = r\"\"\"\n#include <torch/extension.h>\ntorch::Tensor lenet_forward(torch::Tensor x,\n torch::Tensor c1w, torch::Tensor c1b,\n torch::Tensor c2w, torch::Tensor c2b,\n torch::Tensor f1w, torch::Tensor f1b,\n torch::Tensor f2w, torch::Tensor f2b,\n torch::Tensor f3w, torch::Tensor f3b);\n\"\"\"\n\n_src_cuda = 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 lenet_fused_kernel(const float* __restrict__ x,\n const float* __restrict__ c1w, const float* __restrict__ c1b,\n const float* __restrict__ c2w, const float* __restrict__ c2b,\n const float* __restrict__ f1w, const float* __restrict__ f1b,\n const float* __restrict__ f2w, const float* __restrict__ f2b,\n const float* __restrict__ f3w, const float* __restrict__ f3b,\n float* __restrict__ out,\n int K) {\n int n = blockIdx.x;\n int tid = threadIdx.x;\n __shared__ float p1[6*14*14];\n __shared__ float p2[16*5*5];\n __shared__ float h1[120];\n __shared__ float h2[84];\n\n const float* xin = x + ((size_t)n)*32*32;\n\n for (int idx = tid; idx < 6*14*14; idx += blockDim.x) {\n int tmp = idx;\n int px = tmp % 14; tmp /= 14;\n int py = tmp % 14; tmp /= 14;\n int oc = tmp;\n int oy = py * 2;\n int ox = px * 2;\n float s00 = c1b[oc], s01 = s00, s10 = s00, s11 = s00;\n const float* w = c1w + oc*25;\n #pragma unroll\n for (int ky = 0; ky < 5; ++ky) {\n int r0 = (oy + ky) * 32 + ox;\n int r1 = r0 + 32;\n #pragma unroll\n for (int kx = 0; kx < 5; ++kx) {\n float ww = w[ky*5 + kx];\n s00 += ww * xin[r0 + kx];\n s01 += ww * xin[r0 + kx + 1];\n s10 += ww * xin[r1 + kx];\n s11 += ww * xin[r1 + kx + 1];\n }\n }\n float m = 0.0f;\n m = fmaxf(m, s00); m = fmaxf(m, s01); m = fmaxf(m, s10); m = fmaxf(m, s11);\n p1[idx] = m;\n }\n __syncthreads();\n\n for (int idx = tid; idx < 16*5*5; idx += blockDim.x) {\n int tmp = idx;\n int px = tmp % 5; tmp /= 5;\n int py = tmp % 5; tmp /= 5;\n int oc = tmp;\n int oy = py * 2;\n int ox = px * 2;\n float s00 = c2b[oc], s01 = s00, s10 = s00, s11 = s00;\n const float* woc = c2w + oc*6*25;\n #pragma unroll\n for (int ic = 0; ic < 6; ++ic) {\n const float* pp = p1 + ic*14*14;\n const float* wwic = woc + ic*25;\n #pragma unroll\n for (int ky = 0; ky < 5; ++ky) {\n int r0 = (oy + ky) * 14 + ox;\n int r1 = r0 + 14;\n #pragma unroll\n for (int kx = 0; kx < 5; ++kx) {\n float ww = wwic[ky*5 + kx];\n s00 += ww * pp[r0 + kx];\n s01 += ww * pp[r0 + kx + 1];\n s10 += ww * pp[r1 + kx];\n s11 += ww * pp[r1 + kx + 1];\n }\n }\n }\n float m = 0.0f;\n m = fmaxf(m, s00); m = fmaxf(m, s01); m = fmaxf(m, s10); m = fmaxf(m, s11);\n p2[idx] = m;\n }\n __syncthreads();\n\n for (int o = tid; o < 120; o += blockDim.x) {\n float s = f1b[o];\n const float* w = f1w + o*400;\n #pragma unroll 4\n for (int i = 0; i < 400; ++i) s += w[i] * p2[i];\n h1[o] = fmaxf(s, 0.0f);\n }\n __syncthreads();\n\n for (int o = tid; o < 84; o += blockDim.x) {\n float s = f2b[o];\n const float* w = f2w + o*120;\n #pragma unroll 4\n for (int i = 0; i < 120; ++i) s += w[i] * h1[i];\n h2[o] = fmaxf(s, 0.0f);\n }\n __syncthreads();\n\n for (int o = tid; o < K; o += blockDim.x) {\n float s = f3b[o];\n const float* w = f3w + ((size_t)o)*84;\n #pragma unroll\n for (int i = 0; i < 84; ++i) s += w[i] * h2[i];\n out[((size_t)n)*K + o] = s;\n }\n}\n\ntorch::Tensor lenet_forward(torch::Tensor x,\n torch::Tensor c1w, torch::Tensor c1b,\n torch::Tensor c2w, torch::Tensor c2b,\n torch::Tensor f1w, torch::Tensor f1b,\n torch::Tensor f2w, torch::Tensor f2b,\n torch::Tensor f3w, torch::Tensor f3b) {\n const int N = (int)x.size(0);\n const int K = (int)f3b.size(0);\n auto out = torch::empty({N, K}, x.options());\n lenet_fused_kernel<<<N, 256, 0, at::cuda::getCurrentCUDAStream()>>>(\n x.data_ptr<float>(), c1w.data_ptr<float>(), c1b.data_ptr<float>(),\n c2w.data_ptr<float>(), c2b.data_ptr<float>(),\n f1w.data_ptr<float>(), f1b.data_ptr<float>(),\n f2w.data_ptr<float>(), f2b.data_ptr<float>(),\n f3w.data_ptr<float>(), f3b.data_ptr<float>(), out.data_ptr<float>(), K);\n return out;\n}\n\"\"\"\n\n_ext = load_inline(name=\"lenet5_fused_ext_v2\",\n cpp_sources=_src_cpp,\n cuda_sources=_src_cuda,\n functions=[\"lenet_forward\"],\n extra_cuda_cflags=[\"-O3\", \"--use_fast_math\"],\n extra_cflags=[\"-O3\"],\n verbose=False)\n\nclass ModelNew(nn.Module):\n def __init__(self, num_classes):\n super(ModelNew, self).__init__()\n self.conv1 = nn.Conv2d(1, 6, 5, 1)\n self.conv2 = nn.Conv2d(6, 16, 5, 1)\n self.fc1 = nn.Linear(16*5*5, 120)\n self.fc2 = nn.Linear(120, 84)\n self.fc3 = nn.Linear(84, num_classes)\n def forward(self, x):\n return _ext.lenet_forward(x, 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" }
result ← compile_kernel (ok)
okcompile_kernel compile_kernel PASSED: kernel compiled without errors.
turn 4 · 1 tool call(s) · 12.6s turn · 12.6s LLM · 7,372 chars (click to expand)
turn 412.6s turn · 12.6s LLM · 0.0s tools
call → run_correctness
callrun_correctness { "kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\n_src_cpp = r\"\"\"\n#include <torch/extension.h>\ntorch::Tensor lenet_forward(torch::Tensor x,\n torch::Tensor c1w, torch::Tensor c1b,\n torch::Tensor c2w, torch::Tensor c2b,\n torch::Tensor f1w, torch::Tensor f1b,\n torch::Tensor f2w, torch::Tensor f2b,\n torch::Tensor f3w, torch::Tensor f3b);\n\"\"\"\n\n_src_cuda = 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 lenet_fused_kernel(const float* __restrict__ x,\n const float* __restrict__ c1w, const float* __restrict__ c1b,\n const float* __restrict__ c2w, const float* __restrict__ c2b,\n const float* __restrict__ f1w, const float* __restrict__ f1b,\n const float* __restrict__ f2w, const float* __restrict__ f2b,\n const float* __restrict__ f3w, const float* __restrict__ f3b,\n float* __restrict__ out,\n int K) {\n int n = blockIdx.x;\n int tid = threadIdx.x;\n __shared__ float p1[6*14*14];\n __shared__ float p2[16*5*5];\n __shared__ float h1[120];\n __shared__ float h2[84];\n\n const float* xin = x + ((size_t)n)*32*32;\n\n for (int idx = tid; idx < 6*14*14; idx += blockDim.x) {\n int tmp = idx;\n int px = tmp % 14; tmp /= 14;\n int py = tmp % 14; tmp /= 14;\n int oc = tmp;\n int oy = py * 2;\n int ox = px * 2;\n float s00 = c1b[oc], s01 = s00, s10 = s00, s11 = s00;\n const float* w = c1w + oc*25;\n #pragma unroll\n for (int ky = 0; ky < 5; ++ky) {\n int r0 = (oy + ky) * 32 + ox;\n int r1 = r0 + 32;\n #pragma unroll\n for (int kx = 0; kx < 5; ++kx) {\n float ww = w[ky*5 + kx];\n s00 += ww * xin[r0 + kx];\n s01 += ww * xin[r0 + kx + 1];\n s10 += ww * xin[r1 + kx];\n s11 += ww * xin[r1 + kx + 1];\n }\n }\n float m = 0.0f;\n m = fmaxf(m, s00); m = fmaxf(m, s01); m = fmaxf(m, s10); m = fmaxf(m, s11);\n p1[idx] = m;\n }\n __syncthreads();\n\n for (int idx = tid; idx < 16*5*5; idx += blockDim.x) {\n int tmp = idx;\n int px = tmp % 5; tmp /= 5;\n int py = tmp % 5; tmp /= 5;\n int oc = tmp;\n int oy = py * 2;\n int ox = px * 2;\n float s00 = c2b[oc], s01 = s00, s10 = s00, s11 = s00;\n const float* woc = c2w + oc*6*25;\n #pragma unroll\n for (int ic = 0; ic < 6; ++ic) {\n const float* pp = p1 + ic*14*14;\n const float* wwic = woc + ic*25;\n #pragma unroll\n for (int ky = 0; ky < 5; ++ky) {\n int r0 = (oy + ky) * 14 + ox;\n int r1 = r0 + 14;\n #pragma unroll\n for (int kx = 0; kx < 5; ++kx) {\n float ww = wwic[ky*5 + kx];\n s00 += ww * pp[r0 + kx];\n s01 += ww * pp[r0 + kx + 1];\n s10 += ww * pp[r1 + kx];\n s11 += ww * pp[r1 + kx + 1];\n }\n }\n }\n float m = 0.0f;\n m = fmaxf(m, s00); m = fmaxf(m, s01); m = fmaxf(m, s10); m = fmaxf(m, s11);\n p2[idx] = m;\n }\n __syncthreads();\n\n for (int o = tid; o < 120; o += blockDim.x) {\n float s = f1b[o];\n const float* w = f1w + o*400;\n #pragma unroll 4\n for (int i = 0; i < 400; ++i) s += w[i] * p2[i];\n h1[o] = fmaxf(s, 0.0f);\n }\n __syncthreads();\n\n for (int o = tid; o < 84; o += blockDim.x) {\n float s = f2b[o];\n const float* w = f2w + o*120;\n #pragma unroll 4\n for (int i = 0; i < 120; ++i) s += w[i] * h1[i];\n h2[o] = fmaxf(s, 0.0f);\n }\n __syncthreads();\n\n for (int o = tid; o < K; o += blockDim.x) {\n float s = f3b[o];\n const float* w = f3w + ((size_t)o)*84;\n #pragma unroll\n for (int i = 0; i < 84; ++i) s += w[i] * h2[i];\n out[((size_t)n)*K + o] = s;\n }\n}\n\ntorch::Tensor lenet_forward(torch::Tensor x,\n torch::Tensor c1w, torch::Tensor c1b,\n torch::Tensor c2w, torch::Tensor c2b,\n torch::Tensor f1w, torch::Tensor f1b,\n torch::Tensor f2w, torch::Tensor f2b,\n torch::Tensor f3w, torch::Tensor f3b) {\n const int N = (int)x.size(0);\n const int K = (int)f3b.size(0);\n auto out = torch::empty({N, K}, x.options());\n lenet_fused_kernel<<<N, 256, 0, at::cuda::getCurrentCUDAStream()>>>(\n x.data_ptr<float>(), c1w.data_ptr<float>(), c1b.data_ptr<float>(),\n c2w.data_ptr<float>(), c2b.data_ptr<float>(),\n f1w.data_ptr<float>(), f1b.data_ptr<float>(),\n f2w.data_ptr<float>(), f2b.data_ptr<float>(),\n f3w.data_ptr<float>(), f3b.data_ptr<float>(), out.data_ptr<float>(), K);\n return out;\n}\n\"\"\"\n\n_ext = load_inline(name=\"lenet5_fused_ext_v2\",\n cpp_sources=_src_cpp,\n cuda_sources=_src_cuda,\n functions=[\"lenet_forward\"],\n extra_cuda_cflags=[\"-O3\", \"--use_fast_math\"],\n extra_cflags=[\"-O3\"],\n verbose=False)\n\nclass ModelNew(nn.Module):\n def __init__(self, num_classes):\n super(ModelNew, self).__init__()\n self.conv1 = nn.Conv2d(1, 6, 5, 1)\n self.conv2 = nn.Conv2d(6, 16, 5, 1)\n self.fc1 = nn.Linear(16*5*5, 120)\n self.fc2 = nn.Linear(120, 84)\n self.fc3 = nn.Linear(84, num_classes)\n def forward(self, x):\n return _ext.lenet_forward(x, 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" }
result ← run_correctness (ok)
okrun_correctness run_correctness PASSED: (5 / 5) trials all matched the reference. Numerical precision: max_abs_err=7.64e-08 mean_abs_err=1.11e-08 max_rel_err=2.12e-01
turn 5 · 1 tool call(s) · 23.1s turn · 23.1s LLM · 7,684 chars (click to expand)
turn 523.1s turn · 23.1s LLM · 0.0s tools
call → compile_kernel
callcompile_kernel { "kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\n_src_cpp = r\"\"\"\n#include <torch/extension.h>\ntorch::Tensor lenet_forward(torch::Tensor x,\n torch::Tensor c1w, torch::Tensor c1b,\n torch::Tensor c2w, torch::Tensor c2b,\n torch::Tensor f1w, torch::Tensor f1b,\n torch::Tensor f2w, torch::Tensor f2b,\n torch::Tensor f3w, torch::Tensor f3b);\n\"\"\"\n\n_src_cuda = 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 lenet_fused_kernel(const float* __restrict__ x,\n const float* __restrict__ c1w, const float* __restrict__ c1b,\n const float* __restrict__ c2w, const float* __restrict__ c2b,\n const float* __restrict__ f1w, const float* __restrict__ f1b,\n const float* __restrict__ f2w, const float* __restrict__ f2b,\n const float* __restrict__ f3w, const float* __restrict__ f3b,\n float* __restrict__ out,\n int K) {\n int n = blockIdx.x;\n int tid = threadIdx.x;\n __shared__ float p1[6*14*14];\n __shared__ float p2[16*5*5];\n __shared__ float h1[120];\n __shared__ float h2[84];\n __shared__ float sw1[6*25];\n __shared__ float sb1[6];\n __shared__ float sw2[16*6*25];\n __shared__ float sb2[16];\n\n for (int i = tid; i < 6*25; i += blockDim.x) sw1[i] = c1w[i];\n if (tid < 6) sb1[tid] = c1b[tid];\n for (int i = tid; i < 16*6*25; i += blockDim.x) sw2[i] = c2w[i];\n if (tid < 16) sb2[tid] = c2b[tid];\n __syncthreads();\n\n const float* xin = x + ((size_t)n)*32*32;\n\n for (int idx = tid; idx < 6*14*14; idx += blockDim.x) {\n int tmp = idx;\n int px = tmp % 14; tmp /= 14;\n int py = tmp % 14; tmp /= 14;\n int oc = tmp;\n int oy = py * 2;\n int ox = px * 2;\n float b = sb1[oc];\n float s00 = b, s01 = b, s10 = b, s11 = b;\n const float* w = sw1 + oc*25;\n #pragma unroll\n for (int ky = 0; ky < 5; ++ky) {\n int r0 = (oy + ky) * 32 + ox;\n int r1 = r0 + 32;\n #pragma unroll\n for (int kx = 0; kx < 5; ++kx) {\n float ww = w[ky*5 + kx];\n s00 += ww * xin[r0 + kx];\n s01 += ww * xin[r0 + kx + 1];\n s10 += ww * xin[r1 + kx];\n s11 += ww * xin[r1 + kx + 1];\n }\n }\n float m = 0.0f;\n m = fmaxf(m, s00); m = fmaxf(m, s01); m = fmaxf(m, s10); m = fmaxf(m, s11);\n p1[idx] = m;\n }\n __syncthreads();\n\n for (int idx = tid; idx < 16*5*5; idx += blockDim.x) {\n int tmp = idx;\n int px = tmp % 5; tmp /= 5;\n int py = tmp % 5; tmp /= 5;\n int oc = tmp;\n int oy = py * 2;\n int ox = px * 2;\n float b = sb2[oc];\n float s00 = b, s01 = b, s10 = b, s11 = b;\n const float* woc = sw2 + oc*6*25;\n #pragma unroll\n for (int ic = 0; ic < 6; ++ic) {\n const float* pp = p1 + ic*14*14;\n const float* wwic = woc + ic*25;\n #pragma unroll\n for (int ky = 0; ky < 5; ++ky) {\n int r0 = (oy + ky) * 14 + ox;\n int r1 = r0 + 14;\n #pragma unroll\n for (int kx = 0; kx < 5; ++kx) {\n float ww = wwic[ky*5 + kx];\n s00 += ww * pp[r0 + kx];\n s01 += ww * pp[r0 + kx + 1];\n s10 += ww * pp[r1 + kx];\n s11 += ww * pp[r1 + kx + 1];\n }\n }\n }\n float m = 0.0f;\n m = fmaxf(m, s00); m = fmaxf(m, s01); m = fmaxf(m, s10); m = fmaxf(m, s11);\n p2[idx] = m;\n }\n __syncthreads();\n\n for (int o = tid; o < 120; o += blockDim.x) {\n float s = f1b[o];\n const float* w = f1w + o*400;\n #pragma unroll 4\n for (int i = 0; i < 400; ++i) s += w[i] * p2[i];\n h1[o] = fmaxf(s, 0.0f);\n }\n __syncthreads();\n\n for (int o = tid; o < 84; o += blockDim.x) {\n float s = f2b[o];\n const float* w = f2w + o*120;\n #pragma unroll 4\n for (int i = 0; i < 120; ++i) s += w[i] * h1[i];\n h2[o] = fmaxf(s, 0.0f);\n }\n __syncthreads();\n\n for (int o = tid; o < K; o += blockDim.x) {\n float s = f3b[o];\n const float* w = f3w + ((size_t)o)*84;\n #pragma unroll\n for (int i = 0; i < 84; ++i) s += w[i] * h2[i];\n out[((size_t)n)*K + o] = s;\n }\n}\n\ntorch::Tensor lenet_forward(torch::Tensor x,\n torch::Tensor c1w, torch::Tensor c1b,\n torch::Tensor c2w, torch::Tensor c2b,\n torch::Tensor f1w, torch::Tensor f1b,\n torch::Tensor f2w, torch::Tensor f2b,\n torch::Tensor f3w, torch::Tensor f3b) {\n const int N = (int)x.size(0);\n const int K = (int)f3b.size(0);\n auto out = torch::empty({N, K}, x.options());\n lenet_fused_kernel<<<N, 256, 0, at::cuda::getCurrentCUDAStream()>>>(\n x.data_ptr<float>(), c1w.data_ptr<float>(), c1b.data_ptr<float>(),\n c2w.data_ptr<float>(), c2b.data_ptr<float>(),\n f1w.data_ptr<float>(), f1b.data_ptr<float>(),\n f2w.data_ptr<float>(), f2b.data_ptr<float>(),\n f3w.data_ptr<float>(), f3b.data_ptr<float>(), out.data_ptr<float>(), K);\n return out;\n}\n\"\"\"\n\n_ext = load_inline(name=\"lenet5_fused_ext_v3\",\n cpp_sources=_src_cpp,\n cuda_sources=_src_cuda,\n functions=[\"lenet_forward\"],\n extra_cuda_cflags=[\"-O3\", \"--use_fast_math\"],\n extra_cflags=[\"-O3\"],\n verbose=False)\n\nclass ModelNew(nn.Module):\n def __init__(self, num_classes):\n super(ModelNew, self).__init__()\n self.conv1 = nn.Conv2d(1, 6, 5, 1)\n self.conv2 = nn.Conv2d(6, 16, 5, 1)\n self.fc1 = nn.Linear(16*5*5, 120)\n self.fc2 = nn.Linear(120, 84)\n self.fc3 = nn.Linear(84, num_classes)\n def forward(self, x):\n return _ext.lenet_forward(x, 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" }
result ← compile_kernel (ok)
okcompile_kernel compile_kernel PASSED: kernel compiled without errors.
turn 6 · 1 tool call(s) · 14.7s turn · 14.7s LLM · 7,786 chars (click to expand)
turn 614.7s turn · 14.7s LLM · 0.0s tools
call → run_correctness
callrun_correctness { "kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\n_src_cpp = r\"\"\"\n#include <torch/extension.h>\ntorch::Tensor lenet_forward(torch::Tensor x,\n torch::Tensor c1w, torch::Tensor c1b,\n torch::Tensor c2w, torch::Tensor c2b,\n torch::Tensor f1w, torch::Tensor f1b,\n torch::Tensor f2w, torch::Tensor f2b,\n torch::Tensor f3w, torch::Tensor f3b);\n\"\"\"\n\n_src_cuda = 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 lenet_fused_kernel(const float* __restrict__ x,\n const float* __restrict__ c1w, const float* __restrict__ c1b,\n const float* __restrict__ c2w, const float* __restrict__ c2b,\n const float* __restrict__ f1w, const float* __restrict__ f1b,\n const float* __restrict__ f2w, const float* __restrict__ f2b,\n const float* __restrict__ f3w, const float* __restrict__ f3b,\n float* __restrict__ out,\n int K) {\n int n = blockIdx.x;\n int tid = threadIdx.x;\n __shared__ float p1[6*14*14];\n __shared__ float p2[16*5*5];\n __shared__ float h1[120];\n __shared__ float h2[84];\n __shared__ float sw1[6*25];\n __shared__ float sb1[6];\n __shared__ float sw2[16*6*25];\n __shared__ float sb2[16];\n\n for (int i = tid; i < 6*25; i += blockDim.x) sw1[i] = c1w[i];\n if (tid < 6) sb1[tid] = c1b[tid];\n for (int i = tid; i < 16*6*25; i += blockDim.x) sw2[i] = c2w[i];\n if (tid < 16) sb2[tid] = c2b[tid];\n __syncthreads();\n\n const float* xin = x + ((size_t)n)*32*32;\n\n for (int idx = tid; idx < 6*14*14; idx += blockDim.x) {\n int tmp = idx;\n int px = tmp % 14; tmp /= 14;\n int py = tmp % 14; tmp /= 14;\n int oc = tmp;\n int oy = py * 2;\n int ox = px * 2;\n float b = sb1[oc];\n float s00 = b, s01 = b, s10 = b, s11 = b;\n const float* w = sw1 + oc*25;\n #pragma unroll\n for (int ky = 0; ky < 5; ++ky) {\n int r0 = (oy + ky) * 32 + ox;\n int r1 = r0 + 32;\n #pragma unroll\n for (int kx = 0; kx < 5; ++kx) {\n float ww = w[ky*5 + kx];\n s00 += ww * xin[r0 + kx];\n s01 += ww * xin[r0 + kx + 1];\n s10 += ww * xin[r1 + kx];\n s11 += ww * xin[r1 + kx + 1];\n }\n }\n float m = 0.0f;\n m = fmaxf(m, s00); m = fmaxf(m, s01); m = fmaxf(m, s10); m = fmaxf(m, s11);\n p1[idx] = m;\n }\n __syncthreads();\n\n for (int idx = tid; idx < 16*5*5; idx += blockDim.x) {\n int tmp = idx;\n int px = tmp % 5; tmp /= 5;\n int py = tmp % 5; tmp /= 5;\n int oc = tmp;\n int oy = py * 2;\n int ox = px * 2;\n float b = sb2[oc];\n float s00 = b, s01 = b, s10 = b, s11 = b;\n const float* woc = sw2 + oc*6*25;\n #pragma unroll\n for (int ic = 0; ic < 6; ++ic) {\n const float* pp = p1 + ic*14*14;\n const float* wwic = woc + ic*25;\n #pragma unroll\n for (int ky = 0; ky < 5; ++ky) {\n int r0 = (oy + ky) * 14 + ox;\n int r1 = r0 + 14;\n #pragma unroll\n for (int kx = 0; kx < 5; ++kx) {\n float ww = wwic[ky*5 + kx];\n s00 += ww * pp[r0 + kx];\n s01 += ww * pp[r0 + kx + 1];\n s10 += ww * pp[r1 + kx];\n s11 += ww * pp[r1 + kx + 1];\n }\n }\n }\n float m = 0.0f;\n m = fmaxf(m, s00); m = fmaxf(m, s01); m = fmaxf(m, s10); m = fmaxf(m, s11);\n p2[idx] = m;\n }\n __syncthreads();\n\n for (int o = tid; o < 120; o += blockDim.x) {\n float s = f1b[o];\n const float* w = f1w + o*400;\n #pragma unroll 4\n for (int i = 0; i < 400; ++i) s += w[i] * p2[i];\n h1[o] = fmaxf(s, 0.0f);\n }\n __syncthreads();\n\n for (int o = tid; o < 84; o += blockDim.x) {\n float s = f2b[o];\n const float* w = f2w + o*120;\n #pragma unroll 4\n for (int i = 0; i < 120; ++i) s += w[i] * h1[i];\n h2[o] = fmaxf(s, 0.0f);\n }\n __syncthreads();\n\n for (int o = tid; o < K; o += blockDim.x) {\n float s = f3b[o];\n const float* w = f3w + ((size_t)o)*84;\n #pragma unroll\n for (int i = 0; i < 84; ++i) s += w[i] * h2[i];\n out[((size_t)n)*K + o] = s;\n }\n}\n\ntorch::Tensor lenet_forward(torch::Tensor x,\n torch::Tensor c1w, torch::Tensor c1b,\n torch::Tensor c2w, torch::Tensor c2b,\n torch::Tensor f1w, torch::Tensor f1b,\n torch::Tensor f2w, torch::Tensor f2b,\n torch::Tensor f3w, torch::Tensor f3b) {\n const int N = (int)x.size(0);\n const int K = (int)f3b.size(0);\n auto out = torch::empty({N, K}, x.options());\n lenet_fused_kernel<<<N, 256, 0, at::cuda::getCurrentCUDAStream()>>>(\n x.data_ptr<float>(), c1w.data_ptr<float>(), c1b.data_ptr<float>(),\n c2w.data_ptr<float>(), c2b.data_ptr<float>(),\n f1w.data_ptr<float>(), f1b.data_ptr<float>(),\n f2w.data_ptr<float>(), f2b.data_ptr<float>(),\n f3w.data_ptr<float>(), f3b.data_ptr<float>(), out.data_ptr<float>(), K);\n return out;\n}\n\"\"\"\n\n_ext = load_inline(name=\"lenet5_fused_ext_v3\",\n cpp_sources=_src_cpp,\n cuda_sources=_src_cuda,\n functions=[\"lenet_forward\"],\n extra_cuda_cflags=[\"-O3\", \"--use_fast_math\"],\n extra_cflags=[\"-O3\"],\n verbose=False)\n\nclass ModelNew(nn.Module):\n def __init__(self, num_classes):\n super(ModelNew, self).__init__()\n self.conv1 = nn.Conv2d(1, 6, 5, 1)\n self.conv2 = nn.Conv2d(6, 16, 5, 1)\n self.fc1 = nn.Linear(16*5*5, 120)\n self.fc2 = nn.Linear(120, 84)\n self.fc3 = nn.Linear(84, num_classes)\n def forward(self, x):\n return _ext.lenet_forward(x, 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" }
result ← run_correctness (ok)
okrun_correctness run_correctness PASSED: (5 / 5) trials all matched the reference. Numerical precision: max_abs_err=7.64e-08 mean_abs_err=1.11e-08 max_rel_err=2.12e-01
turn 7 · 1 tool call(s) · 40.7s turn · 40.7s LLM · 10,011 chars (click to expand)
turn 740.7s turn · 40.7s LLM · 0.0s tools
reasoning
**Evaluating memory usage** I need to consider static final settings for shared weights per block. If I load 2400 floats, that results in about 39MB of global overhead. Previous weights might be cached in L2, but there are also shared loads and instruction counts to factor in. Loading weights into shared memory vs L2 might not be beneficial. I'm wondering about using constant memory for dynamic weights, but copying could be an issue. Maybe I could process two samples per block with shared weights, but that means doubling the arrays. A block of 512 threads could improve efficiency, but needs careful consideration. **Optimizing weight loading** I'm considering how to optimize weight loading while maintaining occupancy. Each block has 512 threads, but I need to manage samples with groups of 256. If I use shared memory for weights, it could reduce overall loading times. For N=4096, I can reduce block counts and overhead, but double-checking my totals is key. I want to ensure that each block operates independently while still maintaining synchronization. I might need a pointer offset based on the group, and I want to incorporate checks to avoid returning before synchronization finishes, which should be more efficient. **Evaluating block and sample configuration** I'm considering using four samples per block with 1024 threads. That would involve using 4 arrays of 1780 floats plus weights, totaling about 9692 floats, which is around 38.8KB. With 1024 threads, I can run a maximum of 2 blocks per SM, leading to 64 warps. This setup allows me to amortize weights further, but I’m noting that block scheduling becomes less granular. I’ll ensure each sample group is still effectively managed with 256 threads. This could be a solid approach! **Exploring instruction latency** I'm looking into global load instruction latency and its relationship with cache. It seems like each warp could be loading a uniform address only once. Hmm, that gives me something to think about. In version 3, it looks like shared loads are replacing global loads, which is an interesting shift. I wonder how this change will affect performance and efficiency in various applications. There's definitely more to unpack here!
call → static_check
callstatic_check { "kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\n_src_cpp = r\"\"\"\n#include <torch/extension.h>\ntorch::Tensor lenet_forward(torch::Tensor x,\n torch::Tensor c1w, torch::Tensor c1b,\n torch::Tensor c2w, torch::Tensor c2b,\n torch::Tensor f1w, torch::Tensor f1b,\n torch::Tensor f2w, torch::Tensor f2b,\n torch::Tensor f3w, torch::Tensor f3b);\n\"\"\"\n\n_src_cuda = 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 lenet_fused_kernel(const float* __restrict__ x,\n const float* __restrict__ c1w, const float* __restrict__ c1b,\n const float* __restrict__ c2w, const float* __restrict__ c2b,\n const float* __restrict__ f1w, const float* __restrict__ f1b,\n const float* __restrict__ f2w, const float* __restrict__ f2b,\n const float* __restrict__ f3w, const float* __restrict__ f3b,\n float* __restrict__ out,\n int K) {\n int n = blockIdx.x;\n int tid = threadIdx.x;\n __shared__ float p1[6*14*14];\n __shared__ float p2[16*5*5];\n __shared__ float h1[120];\n __shared__ float h2[84];\n __shared__ float sw1[6*25];\n __shared__ float sb1[6];\n __shared__ float sw2[16*6*25];\n __shared__ float sb2[16];\n\n for (int i = tid; i < 6*25; i += blockDim.x) sw1[i] = c1w[i];\n if (tid < 6) sb1[tid] = c1b[tid];\n for (int i = tid; i < 16*6*25; i += blockDim.x) sw2[i] = c2w[i];\n if (tid < 16) sb2[tid] = c2b[tid];\n __syncthreads();\n\n const float* xin = x + ((size_t)n)*32*32;\n\n for (int idx = tid; idx < 6*14*14; idx += blockDim.x) {\n int tmp = idx;\n int px = tmp % 14; tmp /= 14;\n int py = tmp % 14; tmp /= 14;\n int oc = tmp;\n int oy = py * 2;\n int ox = px * 2;\n float b = sb1[oc];\n float s00 = b, s01 = b, s10 = b, s11 = b;\n const float* w = sw1 + oc*25;\n #pragma unroll\n for (int ky = 0; ky < 5; ++ky) {\n int r0 = (oy + ky) * 32 + ox;\n int r1 = r0 + 32;\n #pragma unroll\n for (int kx = 0; kx < 5; ++kx) {\n float ww = w[ky*5 + kx];\n s00 += ww * xin[r0 + kx];\n s01 += ww * xin[r0 + kx + 1];\n s10 += ww * xin[r1 + kx];\n s11 += ww * xin[r1 + kx + 1];\n }\n }\n float m = 0.0f;\n m = fmaxf(m, s00); m = fmaxf(m, s01); m = fmaxf(m, s10); m = fmaxf(m, s11);\n p1[idx] = m;\n }\n __syncthreads();\n\n for (int idx = tid; idx < 16*5*5; idx += blockDim.x) {\n int tmp = idx;\n int px = tmp % 5; tmp /= 5;\n int py = tmp % 5; tmp /= 5;\n int oc = tmp;\n int oy = py * 2;\n int ox = px * 2;\n float b = sb2[oc];\n float s00 = b, s01 = b, s10 = b, s11 = b;\n const float* woc = sw2 + oc*6*25;\n #pragma unroll\n for (int ic = 0; ic < 6; ++ic) {\n const float* pp = p1 + ic*14*14;\n const float* wwic = woc + ic*25;\n #pragma unroll\n for (int ky = 0; ky < 5; ++ky) {\n int r0 = (oy + ky) * 14 + ox;\n int r1 = r0 + 14;\n #pragma unroll\n for (int kx = 0; kx < 5; ++kx) {\n float ww = wwic[ky*5 + kx];\n s00 += ww * pp[r0 + kx];\n s01 += ww * pp[r0 + kx + 1];\n s10 += ww * pp[r1 + kx];\n s11 += ww * pp[r1 + kx + 1];\n }\n }\n }\n float m = 0.0f;\n m = fmaxf(m, s00); m = fmaxf(m, s01); m = fmaxf(m, s10); m = fmaxf(m, s11);\n p2[idx] = m;\n }\n __syncthreads();\n\n for (int o = tid; o < 120; o += blockDim.x) {\n float s = f1b[o];\n const float* w = f1w + o*400;\n #pragma unroll 4\n for (int i = 0; i < 400; ++i) s += w[i] * p2[i];\n h1[o] = fmaxf(s, 0.0f);\n }\n __syncthreads();\n\n for (int o = tid; o < 84; o += blockDim.x) {\n float s = f2b[o];\n const float* w = f2w + o*120;\n #pragma unroll 4\n for (int i = 0; i < 120; ++i) s += w[i] * h1[i];\n h2[o] = fmaxf(s, 0.0f);\n }\n __syncthreads();\n\n for (int o = tid; o < K; o += blockDim.x) {\n float s = f3b[o];\n const float* w = f3w + ((size_t)o)*84;\n #pragma unroll\n for (int i = 0; i < 84; ++i) s += w[i] * h2[i];\n out[((size_t)n)*K + o] = s;\n }\n}\n\ntorch::Tensor lenet_forward(torch::Tensor x,\n torch::Tensor c1w, torch::Tensor c1b,\n torch::Tensor c2w, torch::Tensor c2b,\n torch::Tensor f1w, torch::Tensor f1b,\n torch::Tensor f2w, torch::Tensor f2b,\n torch::Tensor f3w, torch::Tensor f3b) {\n const int N = (int)x.size(0);\n const int K = (int)f3b.size(0);\n auto out = torch::empty({N, K}, x.options());\n lenet_fused_kernel<<<N, 256, 0, at::cuda::getCurrentCUDAStream()>>>(\n x.data_ptr<float>(), c1w.data_ptr<float>(), c1b.data_ptr<float>(),\n c2w.data_ptr<float>(), c2b.data_ptr<float>(),\n f1w.data_ptr<float>(), f1b.data_ptr<float>(),\n f2w.data_ptr<float>(), f2b.data_ptr<float>(),\n f3w.data_ptr<float>(), f3b.data_ptr<float>(), out.data_ptr<float>(), K);\n return out;\n}\n\"\"\"\n\n_ext = load_inline(name=\"lenet5_fused_ext_v3\",\n cpp_sources=_src_cpp,\n cuda_sources=_src_cuda,\n functions=[\"lenet_forward\"],\n extra_cuda_cflags=[\"-O3\", \"--use_fast_math\"],\n extra_cflags=[\"-O3\"],\n verbose=False)\n\nclass ModelNew(nn.Module):\n def __init__(self, num_classes):\n super(ModelNew, self).__init__()\n self.conv1 = nn.Conv2d(1, 6, 5, 1)\n self.conv2 = nn.Conv2d(6, 16, 5, 1)\n self.fc1 = nn.Linear(16*5*5, 120)\n self.fc2 = nn.Linear(120, 84)\n self.fc3 = nn.Linear(84, num_classes)\n def forward(self, x):\n return _ext.lenet_forward(x, 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" }
result ← static_check (ok)
okstatic_check static_check PASSED: no violations or warnings detected.
turn 8 · FINAL · 1 tool call(s) · 16.1s turn · 16.1s LLM · 8,626 chars (click to expand)
turn 816.1s turn · 16.1s LLM · 0.0s tools · FINAL
reasoning
**Finalizing submission details** I need to make my final submission. Before I do that, I should consider using `extra_cuda --use_fast_math`, as it might affect fmax fine and FMA. However, I don't need to try that now. I wonder if there's a potential issue with name collisions across eval. The `load_inline` name is v3, which should be fine. If K isn't 20, the output should still be okay. I might explore if N is variable; maybe I'll adjust the grid shape to 4096. It’s likely that the input is contiguous. I must submit this soon!
call → submit_kernel
callsubmit_kernel { "kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\n_src_cpp = r\"\"\"\n#include <torch/extension.h>\ntorch::Tensor lenet_forward(torch::Tensor x,\n torch::Tensor c1w, torch::Tensor c1b,\n torch::Tensor c2w, torch::Tensor c2b,\n torch::Tensor f1w, torch::Tensor f1b,\n torch::Tensor f2w, torch::Tensor f2b,\n torch::Tensor f3w, torch::Tensor f3b);\n\"\"\"\n\n_src_cuda = 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 lenet_fused_kernel(const float* __restrict__ x,\n const float* __restrict__ c1w, const float* __restrict__ c1b,\n const float* __restrict__ c2w, const float* __restrict__ c2b,\n const float* __restrict__ f1w, const float* __restrict__ f1b,\n const float* __restrict__ f2w, const float* __restrict__ f2b,\n const float* __restrict__ f3w, const float* __restrict__ f3b,\n float* __restrict__ out,\n int K) {\n int n = blockIdx.x;\n int tid = threadIdx.x;\n __shared__ float p1[6*14*14];\n __shared__ float p2[16*5*5];\n __shared__ float h1[120];\n __shared__ float h2[84];\n __shared__ float sw1[6*25];\n __shared__ float sb1[6];\n __shared__ float sw2[16*6*25];\n __shared__ float sb2[16];\n\n for (int i = tid; i < 6*25; i += blockDim.x) sw1[i] = c1w[i];\n if (tid < 6) sb1[tid] = c1b[tid];\n for (int i = tid; i < 16*6*25; i += blockDim.x) sw2[i] = c2w[i];\n if (tid < 16) sb2[tid] = c2b[tid];\n __syncthreads();\n\n const float* xin = x + ((size_t)n)*32*32;\n\n for (int idx = tid; idx < 6*14*14; idx += blockDim.x) {\n int tmp = idx;\n int px = tmp % 14; tmp /= 14;\n int py = tmp % 14; tmp /= 14;\n int oc = tmp;\n int oy = py * 2;\n int ox = px * 2;\n float b = sb1[oc];\n float s00 = b, s01 = b, s10 = b, s11 = b;\n const float* w = sw1 + oc*25;\n #pragma unroll\n for (int ky = 0; ky < 5; ++ky) {\n int r0 = (oy + ky) * 32 + ox;\n int r1 = r0 + 32;\n #pragma unroll\n for (int kx = 0; kx < 5; ++kx) {\n float ww = w[ky*5 + kx];\n s00 += ww * xin[r0 + kx];\n s01 += ww * xin[r0 + kx + 1];\n s10 += ww * xin[r1 + kx];\n s11 += ww * xin[r1 + kx + 1];\n }\n }\n float m = 0.0f;\n m = fmaxf(m, s00); m = fmaxf(m, s01); m = fmaxf(m, s10); m = fmaxf(m, s11);\n p1[idx] = m;\n }\n __syncthreads();\n\n for (int idx = tid; idx < 16*5*5; idx += blockDim.x) {\n int tmp = idx;\n int px = tmp % 5; tmp /= 5;\n int py = tmp % 5; tmp /= 5;\n int oc = tmp;\n int oy = py * 2;\n int ox = px * 2;\n float b = sb2[oc];\n float s00 = b, s01 = b, s10 = b, s11 = b;\n const float* woc = sw2 + oc*6*25;\n #pragma unroll\n for (int ic = 0; ic < 6; ++ic) {\n const float* pp = p1 + ic*14*14;\n const float* wwic = woc + ic*25;\n #pragma unroll\n for (int ky = 0; ky < 5; ++ky) {\n int r0 = (oy + ky) * 14 + ox;\n int r1 = r0 + 14;\n #pragma unroll\n for (int kx = 0; kx < 5; ++kx) {\n float ww = wwic[ky*5 + kx];\n s00 += ww * pp[r0 + kx];\n s01 += ww * pp[r0 + kx + 1];\n s10 += ww * pp[r1 + kx];\n s11 += ww * pp[r1 + kx + 1];\n }\n }\n }\n float m = 0.0f;\n m = fmaxf(m, s00); m = fmaxf(m, s01); m = fmaxf(m, s10); m = fmaxf(m, s11);\n p2[idx] = m;\n }\n __syncthreads();\n\n for (int o = tid; o < 120; o += blockDim.x) {\n float s = f1b[o];\n const float* w = f1w + o*400;\n #pragma unroll 4\n for (int i = 0; i < 400; ++i) s += w[i] * p2[i];\n h1[o] = fmaxf(s, 0.0f);\n }\n __syncthreads();\n\n for (int o = tid; o < 84; o += blockDim.x) {\n float s = f2b[o];\n const float* w = f2w + o*120;\n #pragma unroll 4\n for (int i = 0; i < 120; ++i) s += w[i] * h1[i];\n h2[o] = fmaxf(s, 0.0f);\n }\n __syncthreads();\n\n for (int o = tid; o < K; o += blockDim.x) {\n float s = f3b[o];\n const float* w = f3w + ((size_t)o)*84;\n #pragma unroll\n for (int i = 0; i < 84; ++i) s += w[i] * h2[i];\n out[((size_t)n)*K + o] = s;\n }\n}\n\ntorch::Tensor lenet_forward(torch::Tensor x,\n torch::Tensor c1w, torch::Tensor c1b,\n torch::Tensor c2w, torch::Tensor c2b,\n torch::Tensor f1w, torch::Tensor f1b,\n torch::Tensor f2w, torch::Tensor f2b,\n torch::Tensor f3w, torch::Tensor f3b) {\n const int N = (int)x.size(0);\n const int K = (int)f3b.size(0);\n auto out = torch::empty({N, K}, x.options());\n lenet_fused_kernel<<<N, 256, 0, at::cuda::getCurrentCUDAStream()>>>(\n x.data_ptr<float>(), c1w.data_ptr<float>(), c1b.data_ptr<float>(),\n c2w.data_ptr<float>(), c2b.data_ptr<float>(),\n f1w.data_ptr<float>(), f1b.data_ptr<float>(),\n f2w.data_ptr<float>(), f2b.data_ptr<float>(),\n f3w.data_ptr<float>(), f3b.data_ptr<float>(), out.data_ptr<float>(), K);\n return out;\n}\n\"\"\"\n\n_ext = load_inline(name=\"lenet5_fused_ext_v3\",\n cpp_sources=_src_cpp,\n cuda_sources=_src_cuda,\n functions=[\"lenet_forward\"],\n extra_cuda_cflags=[\"-O3\", \"--use_fast_math\"],\n extra_cflags=[\"-O3\"],\n verbose=False)\n\nclass ModelNew(nn.Module):\n def __init__(self, num_classes):\n super(ModelNew, self).__init__()\n self.conv1 = nn.Conv2d(1, 6, 5, 1)\n self.conv2 = nn.Conv2d(6, 16, 5, 1)\n self.fc1 = nn.Linear(16*5*5, 120)\n self.fc2 = nn.Linear(120, 84)\n self.fc3 = nn.Linear(84, num_classes)\n def forward(self, x):\n return _ext.lenet_forward(x, 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" }
result ← submit_kernel (ok)
oksubmit_kernel submit_kernel PASSED: (5 / 5) correctness trials all passed. Kernel runtime: 0.86 μs Runtime stats: mean=0.86μs median=?μs std=0.04μs Numerical precision: max_abs_err=7.64e-08 mean_abs_err=1.11e-08 max_rel_err=2.12e-01 Memory: 49.79 MB peak (ref: 326.98 MB, ratio: 0.1523x) Kernel launches: 1 (ref: 42, fusion ratio: 42.0) SOL score: 0.067
final submitted kernel (179 lines, 6,666 chars) — full source, untruncated
import torch
import torch.nn as nn
from torch.utils.cpp_extension import load_inline

_src_cpp = r"""
#include <torch/extension.h>
torch::Tensor lenet_forward(torch::Tensor x,
                            torch::Tensor c1w, torch::Tensor c1b,
                            torch::Tensor c2w, torch::Tensor c2b,
                            torch::Tensor f1w, torch::Tensor f1b,
                            torch::Tensor f2w, torch::Tensor f2b,
                            torch::Tensor f3w, torch::Tensor f3b);
"""

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

__global__ void lenet_fused_kernel(const float* __restrict__ x,
                                   const float* __restrict__ c1w, const float* __restrict__ c1b,
                                   const float* __restrict__ c2w, const float* __restrict__ c2b,
                                   const float* __restrict__ f1w, const float* __restrict__ f1b,
                                   const float* __restrict__ f2w, const float* __restrict__ f2b,
                                   const float* __restrict__ f3w, const float* __restrict__ f3b,
                                   float* __restrict__ out,
                                   int K) {
    int n = blockIdx.x;
    int tid = threadIdx.x;
    __shared__ float p1[6*14*14];
    __shared__ float p2[16*5*5];
    __shared__ float h1[120];
    __shared__ float h2[84];
    __shared__ float sw1[6*25];
    __shared__ float sb1[6];
    __shared__ float sw2[16*6*25];
    __shared__ float sb2[16];

    for (int i = tid; i < 6*25; i += blockDim.x) sw1[i] = c1w[i];
    if (tid < 6) sb1[tid] = c1b[tid];
    for (int i = tid; i < 16*6*25; i += blockDim.x) sw2[i] = c2w[i];
    if (tid < 16) sb2[tid] = c2b[tid];
    __syncthreads();

    const float* xin = x + ((size_t)n)*32*32;

    for (int idx = tid; idx < 6*14*14; idx += blockDim.x) {
        int tmp = idx;
        int px = tmp % 14; tmp /= 14;
        int py = tmp % 14; tmp /= 14;
        int oc = tmp;
        int oy = py * 2;
        int ox = px * 2;
        float b = sb1[oc];
        float s00 = b, s01 = b, s10 = b, s11 = b;
        const float* w = sw1 + oc*25;
        #pragma unroll
        for (int ky = 0; ky < 5; ++ky) {
            int r0 = (oy + ky) * 32 + ox;
            int r1 = r0 + 32;
            #pragma unroll
            for (int kx = 0; kx < 5; ++kx) {
                float ww = w[ky*5 + kx];
                s00 += ww * xin[r0 + kx];
                s01 += ww * xin[r0 + kx + 1];
                s10 += ww * xin[r1 + kx];
                s11 += ww * xin[r1 + kx + 1];
            }
        }
        float m = 0.0f;
        m = fmaxf(m, s00); m = fmaxf(m, s01); m = fmaxf(m, s10); m = fmaxf(m, s11);
        p1[idx] = m;
    }
    __syncthreads();

    for (int idx = tid; idx < 16*5*5; idx += blockDim.x) {
        int tmp = idx;
        int px = tmp % 5; tmp /= 5;
        int py = tmp % 5; tmp /= 5;
        int oc = tmp;
        int oy = py * 2;
        int ox = px * 2;
        float b = sb2[oc];
        float s00 = b, s01 = b, s10 = b, s11 = b;
        const float* woc = sw2 + oc*6*25;
        #pragma unroll
        for (int ic = 0; ic < 6; ++ic) {
            const float* pp = p1 + ic*14*14;
            const float* wwic = woc + ic*25;
            #pragma unroll
            for (int ky = 0; ky < 5; ++ky) {
                int r0 = (oy + ky) * 14 + ox;
                int r1 = r0 + 14;
                #pragma unroll
                for (int kx = 0; kx < 5; ++kx) {
                    float ww = wwic[ky*5 + kx];
                    s00 += ww * pp[r0 + kx];
                    s01 += ww * pp[r0 + kx + 1];
                    s10 += ww * pp[r1 + kx];
                    s11 += ww * pp[r1 + kx + 1];
                }
            }
        }
        float m = 0.0f;
        m = fmaxf(m, s00); m = fmaxf(m, s01); m = fmaxf(m, s10); m = fmaxf(m, s11);
        p2[idx] = m;
    }
    __syncthreads();

    for (int o = tid; o < 120; o += blockDim.x) {
        float s = f1b[o];
        const float* w = f1w + o*400;
        #pragma unroll 4
        for (int i = 0; i < 400; ++i) s += w[i] * p2[i];
        h1[o] = fmaxf(s, 0.0f);
    }
    __syncthreads();

    for (int o = tid; o < 84; o += blockDim.x) {
        float s = f2b[o];
        const float* w = f2w + o*120;
        #pragma unroll 4
        for (int i = 0; i < 120; ++i) s += w[i] * h1[i];
        h2[o] = fmaxf(s, 0.0f);
    }
    __syncthreads();

    for (int o = tid; o < K; o += blockDim.x) {
        float s = f3b[o];
        const float* w = f3w + ((size_t)o)*84;
        #pragma unroll
        for (int i = 0; i < 84; ++i) s += w[i] * h2[i];
        out[((size_t)n)*K + o] = s;
    }
}

torch::Tensor lenet_forward(torch::Tensor x,
                            torch::Tensor c1w, torch::Tensor c1b,
                            torch::Tensor c2w, torch::Tensor c2b,
                            torch::Tensor f1w, torch::Tensor f1b,
                            torch::Tensor f2w, torch::Tensor f2b,
                            torch::Tensor f3w, torch::Tensor f3b) {
    const int N = (int)x.size(0);
    const int K = (int)f3b.size(0);
    auto out = torch::empty({N, K}, x.options());
    lenet_fused_kernel<<<N, 256, 0, at::cuda::getCurrentCUDAStream()>>>(
        x.data_ptr<float>(), c1w.data_ptr<float>(), c1b.data_ptr<float>(),
        c2w.data_ptr<float>(), c2b.data_ptr<float>(),
        f1w.data_ptr<float>(), f1b.data_ptr<float>(),
        f2w.data_ptr<float>(), f2b.data_ptr<float>(),
        f3w.data_ptr<float>(), f3b.data_ptr<float>(), out.data_ptr<float>(), K);
    return out;
}
"""

_ext = load_inline(name="lenet5_fused_ext_v3",
                   cpp_sources=_src_cpp,
                   cuda_sources=_src_cuda,
                   functions=["lenet_forward"],
                   extra_cuda_cflags=["-O3", "--use_fast_math"],
                   extra_cflags=["-O3"],
                   verbose=False)

class ModelNew(nn.Module):
    def __init__(self, num_classes):
        super(ModelNew, self).__init__()
        self.conv1 = nn.Conv2d(1, 6, 5, 1)
        self.conv2 = nn.Conv2d(6, 16, 5, 1)
        self.fc1 = nn.Linear(16*5*5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, num_classes)
    def forward(self, x):
        return _ext.lenet_forward(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)