gpt-5.5-priority · original · l123_single_gpt
correct 19_MobileNetV1.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
1.96 μs
ref runtime
2.02 μs
speedup
1.03x
started
2026-05-05T00:39:24Z
timingkernelbench.timing
kernel runtime
1.96 μs
ref runtime
2.02 μs
speedup
1.03x
kernel mean / median / std
1.96 / — / 0.13 μs
ref mean / median / std
2.02 / — / 0.25 μs
numerical precisioneval_kernel_against_ref
max abs error
2.38e-07
mean abs error
3.12e-08
max rel error
0.03
mean rel error
1.49e-06
gpu memoryextended_metrics.measure_memory
kernel peak
147.80 MB
ref peak
147.80 MB
kernel / ref
1.00x
kernel launches / fusiontorch.profiler
kernel launches
487
ref launches
492
fusion ratio (ref / kernel)
1.01x
total cuda time
16720 μs
energyNVML (pynvml)
kernel mJ / run
241.66
ref mJ / run
966.94
ratio (ref / kernel)
4.00x
avg power
146.77 W
ref avg power
580.44 W
SOL (speed-of-light)heuristic_from_speedup
SOL score (0–1)
0.103
dram util %
—
compute util %
—
bottleneck
unknown
dominant pipe
—
arithmetic intensity
—
ridge point
—
roofline / occupancyheuristic
dram bandwidth
—
dram util %
—
fp32 tflops
—
fp32 util %
—
fp16 tflops
—
occupancy %
—
bottleneck
—
dominant pipe %
—
arithmetic intensity
—
ridge point
—
L1 hit %
—
L2 hit %
—
ld sectors / req
—
st sectors / req
—
regs / thread
—
smem / block
—
block size
—
peak bw
—
peak fp32 tflops
—
peak fp16 tflops
—
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, input_channels=3, alpha=1.0):
        """
        MobileNetV1 architecture implementation.

        :param num_classes: The number of output classes (default: 1000)
        :param input_channels: The number of input channels (default: 3 for RGB images)
        :param alpha: Width multiplier (default: 1.0)
        """
        super(Model, self).__init__()
        
        def conv_bn(inp, oup, stride):
            return nn.Sequential(
                nn.Conv2d(inp, oup, 3, stride, 1, bias=False),
                nn.BatchNorm2d(oup),
                nn.ReLU(inplace=True)
            )
        
        def conv_dw(inp, oup, stride):
            return nn.Sequential(
                nn.Conv2d(inp, inp, 3, stride, 1, groups=inp, bias=False),
                nn.BatchNorm2d(inp),
                nn.ReLU(inplace=True),
                
                nn.Conv2d(inp, oup, 1, 1, 0, bias=False),
                nn.BatchNorm2d(oup),
                nn.ReLU(inplace=True),
            )
        
        self.model = nn.Sequential(
            conv_bn(input_channels, int(32 * alpha), 2),
            conv_dw(int(32 * alpha), int(64 * alpha), 1),
            conv_dw(int(64 * alpha), int(128 * alpha), 2),
            conv_dw(int(128 * alpha), int(128 * alpha), 1),
            conv_dw(int(128 * alpha), int(256 * alpha), 2),
            conv_dw(int(256 * alpha), int(256 * alpha), 1),
            conv_dw(int(256 * alpha), int(512 * alpha), 2),
            conv_dw(int(512 * alpha), int(512 * alpha), 1),
            conv_dw(int(512 * alpha), int(512 * alpha), 1),
            conv_dw(int(512 * alpha), int(512 * alpha), 1),
            conv_dw(int(512 * alpha), int(512 * alpha), 1),
            conv_dw(int(512 * alpha), int(512 * alpha), 1),
            conv_dw(int(512 * alpha), int(1024 * alpha), 2),
            conv_dw(int(1024 * alpha), int(1024 * alpha), 1),
            nn.AvgPool2d(7),
        )
        self.fc = nn.Linear(int(1024 * alpha), num_classes)
    
    def forward(self, x):
        """
        :param x: The input tensor, shape (batch_size, input_channels, height, width)
        :return: The output tensor, shape (batch_size, num_classes)
        """
        x = self.model(x)
        x = x.view(x.size(0), -1)
        x = self.fc(x)
        return x

# Test code
batch_size = 10
input_channels = 3
height = 224
width = 224
num_classes = 1000
alpha = 1.0

def get_inputs():
    return [torch.rand(batch_size, input_channels, height, width)]

def get_init_inputs():
    return [num_classes, input_channels, alpha]
turn 0 · FINAL · 1 tool call(s) · 81.3s turn · 81.3s LLM · 14,287 chars (click to expand)
turn 081.3s turn · 81.3s LLM · 0.0s tools · FINAL
reasoning
**Evaluating MobileNet implementation** I need to produce complete Python code to implement MobileNet faster. Should I include weights? It seems like the new model constructor might be independently initialized. For evaluation, I think I need to copy the state_dict. I guess I’ll define the architecture and maybe the modules too. Should I instantiate the new model and load weights to match the reference? It looks like I have to ensure names match, especially for nn.Modules. Setting eval for benchmarking seems necessary, as they typically use fp32. I wonder if I can fuse certain operations to streamline performance. **Optimizing MobileNet with PyTorch** I see that the PyTorch reference includes conv layers, so maybe I should use these for the convolutional parts and perhaps create a custom final layer. It looks like I might need a custom kernel to speed up the process, though it may only offer marginal improvements. I could implement the forward function using self.model[:-1] and fuse avgpool and fc. Although this won't speed things up dramatically, it might still be helpful. Since forward uses PyTorch's conv modules, I'm wondering if partial replacements for the forward computation with a custom CUDA kernel are possible. I'll also consider folding BatchNorm into conv weights at evaluation time to reduce ops. **Inferring model behavior** I need to infer how PyTorch models work. They mention the "forward computation," but there's no clear mention of evaluation. It seems like the models with BatchNorm typically use batch stats, and the evaluation harness likely copies weights and then runs the forward pass. If that's the case, it looks like both train and eval modes might function by default. As long as I define an identical model and customize the final forward pass, it should work for both training and evaluation, especially for the conv and BatchNorm layers. **Considering model precision** I'm thinking about whether to use fp16 or stick with fp32 precision for the Python model. If only fp32 is required, then I'll just focus on building the same modules at that precision level. I'm trying to assess the implications of using these different precisions, as there might be performance and accuracy factors to consider. Overall, I want to ensure I'm working with the right settings for the best results! **Clarifying model structure** I need to define a model that includes avgpool, focusing on layers 0 to 13 and a final custom layer with average pooling and linear. When it comes to training, I should reference conv/bn/relu, and the custom final layer can help save some resources for improved speed. I’m considering whether a custom affine layer could work without training BN. I plan to implement the forward method, looping through the model, while excluding avgpool. This should align with AvgPool and FC with some tolerance.
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>\ntorch::Tensor affine_relu(torch::Tensor x, torch::Tensor gamma, torch::Tensor beta, torch::Tensor mean, torch::Tensor var, double eps);\ntorch::Tensor depthwise_conv_bn_relu(torch::Tensor x, torch::Tensor w, torch::Tensor gamma, torch::Tensor beta, torch::Tensor mean, torch::Tensor var, int64_t stride, double eps);\ntorch::Tensor avgpool_linear(torch::Tensor x, torch::Tensor w, torch::Tensor b);\n'''\n\n_cuda_src = r'''\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n\n__global__ void affine_relu_kernel(float* __restrict__ x,\n const float* __restrict__ gamma,\n const float* __restrict__ beta,\n const float* __restrict__ mean,\n const float* __restrict__ var,\n int total, int C, int HW, float eps) {\n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n if (idx >= total) return;\n int c = (idx / HW) % C;\n float scale = gamma[c] * rsqrtf(var[c] + eps);\n float y = x[idx] * scale + (beta[c] - mean[c] * scale);\n x[idx] = y > 0.0f ? y : 0.0f;\n}\n\ntorch::Tensor affine_relu(torch::Tensor x, torch::Tensor gamma, torch::Tensor beta,\n torch::Tensor mean, torch::Tensor var, double eps) {\n int N = (int)x.size(0), C = (int)x.size(1), H = (int)x.size(2), W = (int)x.size(3);\n int total = N * C * H * W;\n const int threads = 256;\n affine_relu_kernel<<<(total + threads - 1) / threads, threads>>>(\n x.data_ptr<float>(), gamma.data_ptr<float>(), beta.data_ptr<float>(),\n mean.data_ptr<float>(), var.data_ptr<float>(), total, C, H * W, (float)eps);\n return x;\n}\n\n__global__ void depthwise_kernel(const float* __restrict__ x,\n const float* __restrict__ w,\n const float* __restrict__ gamma,\n const float* __restrict__ beta,\n const float* __restrict__ mean,\n const float* __restrict__ var,\n float* __restrict__ y,\n int total, int C, int H, int Wd, int Ho, int Wo,\n int stride, float eps) {\n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n if (idx >= total) return;\n int ow = idx % Wo;\n int t = idx / Wo;\n int oh = t % Ho;\n t /= Ho;\n int c = t % C;\n int n = t / C;\n\n int ih0 = oh * stride - 1;\n int iw0 = ow * stride - 1;\n const float* xp = x + ((n * C + c) * H) * Wd;\n const float* wp = w + c * 9;\n float acc = 0.0f;\n#pragma unroll\n for (int ky = 0; ky < 3; ++ky) {\n int ih = ih0 + ky;\n if ((unsigned)ih < (unsigned)H) {\n#pragma unroll\n for (int kx = 0; kx < 3; ++kx) {\n int iw = iw0 + kx;\n if ((unsigned)iw < (unsigned)Wd) {\n acc += xp[ih * Wd + iw] * wp[ky * 3 + kx];\n }\n }\n }\n }\n float scale = gamma[c] * rsqrtf(var[c] + eps);\n acc = acc * scale + (beta[c] - mean[c] * scale);\n y[idx] = acc > 0.0f ? acc : 0.0f;\n}\n\ntorch::Tensor depthwise_conv_bn_relu(torch::Tensor x, torch::Tensor w,\n torch::Tensor gamma, torch::Tensor beta,\n torch::Tensor mean, torch::Tensor var,\n int64_t stride, double eps) {\n int N = (int)x.size(0), C = (int)x.size(1), H = (int)x.size(2), Wd = (int)x.size(3);\n int s = (int)stride;\n int Ho = (H - 1) / s + 1;\n int Wo = (Wd - 1) / s + 1;\n auto y = torch::empty({N, C, Ho, Wo}, x.options());\n int total = N * C * Ho * Wo;\n const int threads = 256;\n depthwise_kernel<<<(total + threads - 1) / threads, threads>>>(\n x.data_ptr<float>(), w.data_ptr<float>(), gamma.data_ptr<float>(), beta.data_ptr<float>(),\n mean.data_ptr<float>(), var.data_ptr<float>(), y.data_ptr<float>(), total, C, H, Wd, Ho, Wo, s, (float)eps);\n return y;\n}\n\n__global__ void avg_kernel(const float* __restrict__ x, float* __restrict__ avg,\n int NC, int H, int Wd) {\n int idx = blockIdx.x;\n int tid = threadIdx.x;\n int HW = H * Wd;\n float s = 0.0f;\n const float* xp = x + idx * HW;\n for (int i = tid; i < HW; i += blockDim.x) s += xp[i];\n __shared__ float sm[64];\n sm[tid] = s;\n __syncthreads();\n if (tid < 32) sm[tid] += sm[tid + 32];\n __syncthreads();\n if (tid < 16) sm[tid] += sm[tid + 16];\n __syncthreads();\n if (tid < 8) sm[tid] += sm[tid + 8];\n __syncthreads();\n if (tid < 4) sm[tid] += sm[tid + 4];\n __syncthreads();\n if (tid < 2) sm[tid] += sm[tid + 2];\n __syncthreads();\n if (tid == 0) avg[idx] = (sm[0] + sm[1]) / (float)HW;\n}\n\n__global__ void linear_kernel(const float* __restrict__ avg,\n const float* __restrict__ w,\n const float* __restrict__ b,\n float* __restrict__ out,\n int N, int C, int K) {\n int k = blockIdx.x;\n int n = blockIdx.y;\n int tid = threadIdx.x;\n float s = 0.0f;\n const float* ap = avg + n * C;\n const float* wp = w + k * C;\n for (int c = tid; c < C; c += blockDim.x) s += ap[c] * wp[c];\n __shared__ float sm[256];\n sm[tid] = s;\n __syncthreads();\n for (int off = 128; off > 0; off >>= 1) {\n if (tid < off) sm[tid] += sm[tid + off];\n __syncthreads();\n }\n if (tid == 0) out[n * K + k] = sm[0] + b[k];\n}\n\ntorch::Tensor avgpool_linear(torch::Tensor x, torch::Tensor w, torch::Tensor b) {\n int N = (int)x.size(0), C = (int)x.size(1), H = (int)x.size(2), Wd = (int)x.size(3);\n int K = (int)w.size(0);\n auto avg = torch::empty({N, C}, x.options());\n auto out = torch::empty({N, K}, x.options());\n avg_kernel<<<N * C, 64>>>(x.data_ptr<float>(), avg.data_ptr<float>(), N * C, H, Wd);\n dim3 grid(K, N);\n linear_kernel<<<grid, 256>>>(avg.data_ptr<float>(), w.data_ptr<float>(), b.data_ptr<float>(), out.data_ptr<float>(), N, C, K);\n return out;\n}\n'''\n\n_mobilenet_ext = load_inline(\n name='mobilenetv1_fused_ext_v1',\n cpp_sources=_cpp_src,\n cuda_sources=_cuda_src,\n functions=['affine_relu', 'depthwise_conv_bn_relu', 'avgpool_linear'],\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=1000, input_channels=3, alpha=1.0):\n super(ModelNew, self).__init__()\n def conv_bn(inp, oup, stride):\n return nn.Sequential(\n nn.Conv2d(inp, oup, 3, stride, 1, bias=False),\n nn.BatchNorm2d(oup),\n nn.ReLU(inplace=True)\n )\n def conv_dw(inp, oup, stride):\n return nn.Sequential(\n nn.Conv2d(inp, inp, 3, stride, 1, groups=inp, bias=False),\n nn.BatchNorm2d(inp),\n nn.ReLU(inplace=True),\n nn.Conv2d(inp, oup, 1, 1, 0, bias=False),\n nn.BatchNorm2d(oup),\n nn.ReLU(inplace=True),\n )\n self.model = nn.Sequential(\n conv_bn(input_channels, int(32 * alpha), 2),\n conv_dw(int(32 * alpha), int(64 * alpha), 1),\n conv_dw(int(64 * alpha), int(128 * alpha), 2),\n conv_dw(int(128 * alpha), int(128 * alpha), 1),\n conv_dw(int(128 * alpha), int(256 * alpha), 2),\n conv_dw(int(256 * alpha), int(256 * alpha), 1),\n conv_dw(int(256 * alpha), int(512 * alpha), 2),\n conv_dw(int(512 * alpha), int(512 * alpha), 1),\n conv_dw(int(512 * alpha), int(512 * alpha), 1),\n conv_dw(int(512 * alpha), int(512 * alpha), 1),\n conv_dw(int(512 * alpha), int(512 * alpha), 1),\n conv_dw(int(512 * alpha), int(512 * alpha), 1),\n conv_dw(int(512 * alpha), int(1024 * alpha), 2),\n conv_dw(int(1024 * alpha), int(1024 * alpha), 1),\n nn.AvgPool2d(7),\n )\n self.fc = nn.Linear(int(1024 * alpha), num_classes)\n\n def _eval_forward(self, x):\n x = x.contiguous()\n m0 = self.model[0]\n x = F.conv2d(x, m0[0].weight, None, stride=m0[0].stride, padding=m0[0].padding, dilation=m0[0].dilation, groups=1)\n bn = m0[1]\n x = _mobilenet_ext.affine_relu(x, bn.weight, bn.bias, bn.running_mean, bn.running_var, bn.eps)\n for i in range(1, 14):\n blk = self.model[i]\n dw = blk[0]; bn1 = blk[1]\n x = _mobilenet_ext.depthwise_conv_bn_relu(x, dw.weight, bn1.weight, bn1.bias, bn1.running_mean, bn1.running_var, dw.stride[0], bn1.eps)\n pw = blk[3]; bn2 = blk[4]\n x = F.conv2d(x, pw.weight, None, stride=1, padding=0, dilation=1, groups=1)\n x = _mobilenet_ext.affine_relu(x, bn2.weight, bn2.bias, bn2.running_mean, bn2.running_var, bn2.eps)\n return _mobilenet_ext.avgpool_linear(x, self.fc.weight, self.fc.bias)\n\n def forward(self, x):\n if (not self.training) and x.is_cuda and x.dtype is torch.float32:\n return self._eval_forward(x)\n # Training mode must preserve BatchNorm batch-statistics and running-stat updates.\n for i in range(14):\n x = self.model[i](x)\n if x.is_cuda and x.dtype is torch.float32:\n return _mobilenet_ext.avgpool_linear(x.contiguous(), self.fc.weight, self.fc.bias)\n x = self.model[14](x)\n x = x.view(x.size(0), -1)\n return self.fc(x)\n" }
result ← submit_kernel (ok)
oksubmit_kernel submit_kernel PASSED: (5 / 5) correctness trials all passed. Kernel runtime: 1.96 μs Runtime stats: mean=1.96μs median=?μs std=0.13μs Numerical precision: max_abs_err=2.38e-07 mean_abs_err=3.12e-08 max_rel_err=2.74e-02 Memory: 147.8 MB peak (ref: 147.8 MB, ratio: 1.0x) Kernel launches: 487 (ref: 492, fusion ratio: 1.0103) Energy: 241.66 mJ/run (ref: 966.94 mJ, ratio: 4.0012x) SOL score: 0.1031
final submitted kernel (232 lines, 9,752 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 affine_relu(torch::Tensor x, torch::Tensor gamma, torch::Tensor beta, torch::Tensor mean, torch::Tensor var, double eps);
torch::Tensor depthwise_conv_bn_relu(torch::Tensor x, torch::Tensor w, torch::Tensor gamma, torch::Tensor beta, torch::Tensor mean, torch::Tensor var, int64_t stride, double eps);
torch::Tensor avgpool_linear(torch::Tensor x, torch::Tensor w, torch::Tensor b);
'''

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

__global__ void affine_relu_kernel(float* __restrict__ x,
                                   const float* __restrict__ gamma,
                                   const float* __restrict__ beta,
                                   const float* __restrict__ mean,
                                   const float* __restrict__ var,
                                   int total, int C, int HW, float eps) {
    int idx = blockIdx.x * blockDim.x + threadIdx.x;
    if (idx >= total) return;
    int c = (idx / HW) % C;
    float scale = gamma[c] * rsqrtf(var[c] + eps);
    float y = x[idx] * scale + (beta[c] - mean[c] * scale);
    x[idx] = y > 0.0f ? y : 0.0f;
}

torch::Tensor affine_relu(torch::Tensor x, torch::Tensor gamma, torch::Tensor beta,
                          torch::Tensor mean, torch::Tensor var, double eps) {
    int N = (int)x.size(0), C = (int)x.size(1), H = (int)x.size(2), W = (int)x.size(3);
    int total = N * C * H * W;
    const int threads = 256;
    affine_relu_kernel<<<(total + threads - 1) / threads, threads>>>(
        x.data_ptr<float>(), gamma.data_ptr<float>(), beta.data_ptr<float>(),
        mean.data_ptr<float>(), var.data_ptr<float>(), total, C, H * W, (float)eps);
    return x;
}

__global__ void depthwise_kernel(const float* __restrict__ x,
                                 const float* __restrict__ w,
                                 const float* __restrict__ gamma,
                                 const float* __restrict__ beta,
                                 const float* __restrict__ mean,
                                 const float* __restrict__ var,
                                 float* __restrict__ y,
                                 int total, int C, int H, int Wd, int Ho, int Wo,
                                 int stride, float eps) {
    int idx = blockIdx.x * blockDim.x + threadIdx.x;
    if (idx >= total) return;
    int ow = idx % Wo;
    int t = idx / Wo;
    int oh = t % Ho;
    t /= Ho;
    int c = t % C;
    int n = t / C;

    int ih0 = oh * stride - 1;
    int iw0 = ow * stride - 1;
    const float* xp = x + ((n * C + c) * H) * Wd;
    const float* wp = w + c * 9;
    float acc = 0.0f;
#pragma unroll
    for (int ky = 0; ky < 3; ++ky) {
        int ih = ih0 + ky;
        if ((unsigned)ih < (unsigned)H) {
#pragma unroll
            for (int kx = 0; kx < 3; ++kx) {
                int iw = iw0 + kx;
                if ((unsigned)iw < (unsigned)Wd) {
                    acc += xp[ih * Wd + iw] * wp[ky * 3 + kx];
                }
            }
        }
    }
    float scale = gamma[c] * rsqrtf(var[c] + eps);
    acc = acc * scale + (beta[c] - mean[c] * scale);
    y[idx] = acc > 0.0f ? acc : 0.0f;
}

torch::Tensor depthwise_conv_bn_relu(torch::Tensor x, torch::Tensor w,
                                     torch::Tensor gamma, torch::Tensor beta,
                                     torch::Tensor mean, torch::Tensor var,
                                     int64_t stride, double eps) {
    int N = (int)x.size(0), C = (int)x.size(1), H = (int)x.size(2), Wd = (int)x.size(3);
    int s = (int)stride;
    int Ho = (H - 1) / s + 1;
    int Wo = (Wd - 1) / s + 1;
    auto y = torch::empty({N, C, Ho, Wo}, x.options());
    int total = N * C * Ho * Wo;
    const int threads = 256;
    depthwise_kernel<<<(total + threads - 1) / threads, threads>>>(
        x.data_ptr<float>(), w.data_ptr<float>(), gamma.data_ptr<float>(), beta.data_ptr<float>(),
        mean.data_ptr<float>(), var.data_ptr<float>(), y.data_ptr<float>(), total, C, H, Wd, Ho, Wo, s, (float)eps);
    return y;
}

__global__ void avg_kernel(const float* __restrict__ x, float* __restrict__ avg,
                           int NC, int H, int Wd) {
    int idx = blockIdx.x;
    int tid = threadIdx.x;
    int HW = H * Wd;
    float s = 0.0f;
    const float* xp = x + idx * HW;
    for (int i = tid; i < HW; i += blockDim.x) s += xp[i];
    __shared__ float sm[64];
    sm[tid] = s;
    __syncthreads();
    if (tid < 32) sm[tid] += sm[tid + 32];
    __syncthreads();
    if (tid < 16) sm[tid] += sm[tid + 16];
    __syncthreads();
    if (tid < 8) sm[tid] += sm[tid + 8];
    __syncthreads();
    if (tid < 4) sm[tid] += sm[tid + 4];
    __syncthreads();
    if (tid < 2) sm[tid] += sm[tid + 2];
    __syncthreads();
    if (tid == 0) avg[idx] = (sm[0] + sm[1]) / (float)HW;
}

__global__ void linear_kernel(const float* __restrict__ avg,
                              const float* __restrict__ w,
                              const float* __restrict__ b,
                              float* __restrict__ out,
                              int N, int C, int K) {
    int k = blockIdx.x;
    int n = blockIdx.y;
    int tid = threadIdx.x;
    float s = 0.0f;
    const float* ap = avg + n * C;
    const float* wp = w + k * C;
    for (int c = tid; c < C; c += blockDim.x) s += ap[c] * wp[c];
    __shared__ float sm[256];
    sm[tid] = s;
    __syncthreads();
    for (int off = 128; off > 0; off >>= 1) {
        if (tid < off) sm[tid] += sm[tid + off];
        __syncthreads();
    }
    if (tid == 0) out[n * K + k] = sm[0] + b[k];
}

torch::Tensor avgpool_linear(torch::Tensor x, torch::Tensor w, torch::Tensor b) {
    int N = (int)x.size(0), C = (int)x.size(1), H = (int)x.size(2), Wd = (int)x.size(3);
    int K = (int)w.size(0);
    auto avg = torch::empty({N, C}, x.options());
    auto out = torch::empty({N, K}, x.options());
    avg_kernel<<<N * C, 64>>>(x.data_ptr<float>(), avg.data_ptr<float>(), N * C, H, Wd);
    dim3 grid(K, N);
    linear_kernel<<<grid, 256>>>(avg.data_ptr<float>(), w.data_ptr<float>(), b.data_ptr<float>(), out.data_ptr<float>(), N, C, K);
    return out;
}
'''

_mobilenet_ext = load_inline(
    name='mobilenetv1_fused_ext_v1',
    cpp_sources=_cpp_src,
    cuda_sources=_cuda_src,
    functions=['affine_relu', 'depthwise_conv_bn_relu', 'avgpool_linear'],
    extra_cflags=['-O3'],
    extra_cuda_cflags=['-O3', '--use_fast_math'],
    verbose=False,
)

class ModelNew(nn.Module):
    def __init__(self, num_classes=1000, input_channels=3, alpha=1.0):
        super(ModelNew, self).__init__()
        def conv_bn(inp, oup, stride):
            return nn.Sequential(
                nn.Conv2d(inp, oup, 3, stride, 1, bias=False),
                nn.BatchNorm2d(oup),
                nn.ReLU(inplace=True)
            )
        def conv_dw(inp, oup, stride):
            return nn.Sequential(
                nn.Conv2d(inp, inp, 3, stride, 1, groups=inp, bias=False),
                nn.BatchNorm2d(inp),
                nn.ReLU(inplace=True),
                nn.Conv2d(inp, oup, 1, 1, 0, bias=False),
                nn.BatchNorm2d(oup),
                nn.ReLU(inplace=True),
            )
        self.model = nn.Sequential(
            conv_bn(input_channels, int(32 * alpha), 2),
            conv_dw(int(32 * alpha), int(64 * alpha), 1),
            conv_dw(int(64 * alpha), int(128 * alpha), 2),
            conv_dw(int(128 * alpha), int(128 * alpha), 1),
            conv_dw(int(128 * alpha), int(256 * alpha), 2),
            conv_dw(int(256 * alpha), int(256 * alpha), 1),
            conv_dw(int(256 * alpha), int(512 * alpha), 2),
            conv_dw(int(512 * alpha), int(512 * alpha), 1),
            conv_dw(int(512 * alpha), int(512 * alpha), 1),
            conv_dw(int(512 * alpha), int(512 * alpha), 1),
            conv_dw(int(512 * alpha), int(512 * alpha), 1),
            conv_dw(int(512 * alpha), int(512 * alpha), 1),
            conv_dw(int(512 * alpha), int(1024 * alpha), 2),
            conv_dw(int(1024 * alpha), int(1024 * alpha), 1),
            nn.AvgPool2d(7),
        )
        self.fc = nn.Linear(int(1024 * alpha), num_classes)

    def _eval_forward(self, x):
        x = x.contiguous()
        m0 = self.model[0]
        x = F.conv2d(x, m0[0].weight, None, stride=m0[0].stride, padding=m0[0].padding, dilation=m0[0].dilation, groups=1)
        bn = m0[1]
        x = _mobilenet_ext.affine_relu(x, bn.weight, bn.bias, bn.running_mean, bn.running_var, bn.eps)
        for i in range(1, 14):
            blk = self.model[i]
            dw = blk[0]; bn1 = blk[1]
            x = _mobilenet_ext.depthwise_conv_bn_relu(x, dw.weight, bn1.weight, bn1.bias, bn1.running_mean, bn1.running_var, dw.stride[0], bn1.eps)
            pw = blk[3]; bn2 = blk[4]
            x = F.conv2d(x, pw.weight, None, stride=1, padding=0, dilation=1, groups=1)
            x = _mobilenet_ext.affine_relu(x, bn2.weight, bn2.bias, bn2.running_mean, bn2.running_var, bn2.eps)
        return _mobilenet_ext.avgpool_linear(x, self.fc.weight, self.fc.bias)

    def forward(self, x):
        if (not self.training) and x.is_cuda and x.dtype is torch.float32:
            return self._eval_forward(x)
        # Training mode must preserve BatchNorm batch-statistics and running-stat updates.
        for i in range(14):
            x = self.model[i](x)
        if x.is_cuda and x.dtype is torch.float32:
            return _mobilenet_ext.avgpool_linear(x.contiguous(), self.fc.weight, self.fc.bias)
        x = self.model[14](x)
        x = x.view(x.size(0), -1)
        return self.fc(x)