gpt-5.5-priority · original · l123_single_gpt
compile_fail 23_EfficientNetB1.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
—
ref runtime
-1.00 μs
speedup
—
started
2026-05-05T01:18:22Z
compile error: Error building extension 'effnet_b1_tail_ext_v1': [1/3] /opt/conda/bin/x86_64-conda-linux-gnu-c++ -MMD -MF main.o.d -DTORCH_EXTENSION_NAME=effnet_b1_tail_ext_v1 -DTORCH_API_INCLUDE_EXTENSION_H -isystem /scratch/abaner/KernelBench/.venv/lib/python3.10/site-packages/torch/include -isystem /scratch/abaner/KernelBench/.venv/lib/python3.10/site-packages/torch/include/torch/csrc/api/include -isystem /usr/local/cuda/include -isystem /opt/conda/include/python3.10 -fPIC -std=c++17 -O3 -c /scratch/abaner/KernelBench/runs/l123_single_gpt/original/gpt-5.5-priority/level_3_problem_23_cache/k_c2a304d85ffd/e
timingkernelbench.timing
kernel runtime
—
ref runtime
—
speedup
—
kernel mean / median / std
—
ref mean / median / std
—
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):
        """
        EfficientNetB1 architecture implementation.

        :param num_classes: The number of output classes (default is 1000 for ImageNet).
        """
        super(Model, self).__init__()
        
        # Initial convolutional layer
        self.conv1 = nn.Conv2d(3, 32, kernel_size=3, stride=2, padding=1, bias=False)
        self.bn1 = nn.BatchNorm2d(32)
        
        # MBConv blocks
        self.mbconv1 = self._make_mbconv_block(32, 16, 1, 1)
        self.mbconv2 = self._make_mbconv_block(16, 24, 2, 6)
        self.mbconv3 = self._make_mbconv_block(24, 40, 2, 6)
        self.mbconv4 = self._make_mbconv_block(40, 80, 2, 6)
        self.mbconv5 = self._make_mbconv_block(80, 112, 1, 6)
        self.mbconv6 = self._make_mbconv_block(112, 192, 2, 6)
        self.mbconv7 = self._make_mbconv_block(192, 320, 1, 6)
        
        # Final convolutional layer
        self.conv2 = nn.Conv2d(320, 1280, kernel_size=1, stride=1, padding=0, bias=False)
        self.bn2 = nn.BatchNorm2d(1280)
        
        # Fully connected layer
        self.fc = nn.Linear(1280, num_classes)
    
    def _make_mbconv_block(self, in_channels, out_channels, stride, expand_ratio):
        """
        Creates a MBConv block.

        :param in_channels: Number of input channels.
        :param out_channels: Number of output channels.
        :param stride: Stride of the depthwise convolution.
        :param expand_ratio: Expansion ratio for the hidden layer.
        :return: A sequential MBConv block.
        """
        hidden_dim = round(in_channels * expand_ratio)
        return nn.Sequential(
            nn.Conv2d(in_channels, hidden_dim, kernel_size=1, stride=1, padding=0, bias=False),
            nn.BatchNorm2d(hidden_dim),
            nn.ReLU6(inplace=True),
            nn.Conv2d(hidden_dim, hidden_dim, kernel_size=3, stride=stride, padding=1, groups=hidden_dim, bias=False),
            nn.BatchNorm2d(hidden_dim),
            nn.ReLU6(inplace=True),
            nn.Conv2d(hidden_dim, out_channels, kernel_size=1, stride=1, padding=0, bias=False),
            nn.BatchNorm2d(out_channels),
        )
    
    def forward(self, x):
        """
        Forward pass of the EfficientNetB1 model.

        :param x: Input tensor, shape (batch_size, 3, 240, 240)
        :return: Output tensor, shape (batch_size, num_classes)
        """
        x = F.relu(self.bn1(self.conv1(x)))
        
        x = self.mbconv1(x)
        x = self.mbconv2(x)
        x = self.mbconv3(x)
        x = self.mbconv4(x)
        x = self.mbconv5(x)
        x = self.mbconv6(x)
        x = self.mbconv7(x)
        
        x = F.relu(self.bn2(self.conv2(x)))
        x = F.adaptive_avg_pool2d(x, (1, 1))
        x = torch.flatten(x, 1)
        x = self.fc(x)
        
        return x

# Test code
batch_size = 10
input_shape = (3, 240, 240)
num_classes = 1000

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

def get_init_inputs():
    return [num_classes]
turn 0 · FINAL · 1 tool call(s) · 44.6s turn · 44.6s LLM · 14,847 chars (click to expand)
turn 044.6s turn · 44.6s LLM · 0.0s tools · FINAL
reasoning
**Calculating sizes** I’m working on computing various sizes step by step. Starting with an input of 240, and the first convolution layer with a stride of 2 gives me 120. The next layer (mb1) also results in 120. As I continue, mb2 with a stride of 2 goes down to 60, then mb3 to 30, mb4 to 15, and further down mb5 remains at 15. I determine mb6’s size to be 8 using the ceiling formula. Finally, mb7 is 8, leading to conv2's output dimensions of 1280 x 8 x 8. **Considering implementation options** I'm weighing the idea of implementing a custom `self.tail` evaluation versus relying on PyTorch's built-in tail for correctness, which might serve as a fallback. It’s interesting to think about whether correctness could run during evaluation. I’m also considering the possibility of using Torch for training since it may enhance safety, while keeping runtime tied to evaluation. There are some complexities here, but I’m keen to explore the best approach! **Evaluating precision differences** I'm considering the differences between using fp32 and TF32 in custom implementations. It seems like the differences could exceed a tolerance of 1e-4? For instance, in PyTorch, conv2/linear might use TF32 by default, but the evaluation tolerance is set to 1e-4 as well. The default settings for TF32 in PyTorch 2 also come into play, where matmul has a default of False while conv remains True. I wonder if I can use __float_to_tf32 to mimic TF32 effectively? **Exploring BatchNorm implementation** I'm examining how BatchNorm utilizes unbiased variance in its computations. During the forward pass, it typically uses biased variance for normalization while updating with unbiased variance. I wonder whether I should compute y at each spatial location, or perhaps use a fallback method. If x is on CUDA and not in training mode, I’ll need to implement the tail extension. However, if it's in training mode, I think the fallback approach might work, but it could slow down benchmarks. It seems like many tasks run the model without calling eval, which I find interesting. **Considering model evaluation** I'm wondering if I could set self.eval() in the constructor for the model, which would then reference training to prevent any output mismatches. It seems like evaluators usually set the model to eval mode for inference, especially in benchmark tasks. They mention "forward computation" and a lack of gradients, which makes me think I need to ensure this is handled. For now, it looks like I don't need any final text; I should just submit it and include the extension compile during module loading.
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_cuda_src = r'''\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <vector>\n\n__global__ void spatial_mean_kernel(const float* __restrict__ x, float* __restrict__ m,\n int N, int C, int H, int W) {\n int idx = blockIdx.x;\n int c = idx % C;\n int n = idx / C;\n int HW = H * W;\n const float* p = x + ((n * C + c) * H * W);\n float s = 0.0f;\n // H*W is normally 64 here; keep generic.\n for (int i = threadIdx.x; i < HW; i += blockDim.x) s += p[i];\n __shared__ float sm[128];\n sm[threadIdx.x] = s;\n __syncthreads();\n for (int off = blockDim.x >> 1; off > 0; off >>= 1) {\n if (threadIdx.x < off) sm[threadIdx.x] += sm[threadIdx.x + off];\n __syncthreads();\n }\n if (threadIdx.x == 0) m[n * C + c] = sm[0] / (float)HW;\n}\n\n__global__ void hidden_kernel(const float* __restrict__ m,\n const float* __restrict__ cw,\n const float* __restrict__ bw,\n const float* __restrict__ bb,\n const float* __restrict__ brm,\n const float* __restrict__ brv,\n float* __restrict__ h,\n int N, int C, int K, float eps) {\n int idx = blockIdx.x;\n int k = idx % K;\n int n = idx / K;\n float sum = 0.0f;\n const float* mp = m + n * C;\n const float* wp = cw + k * C;\n for (int c = threadIdx.x; c < C; c += blockDim.x) {\n sum += mp[c] * wp[c];\n }\n __shared__ float sm[256];\n sm[threadIdx.x] = sum;\n __syncthreads();\n for (int off = blockDim.x >> 1; off > 0; off >>= 1) {\n if (threadIdx.x < off) sm[threadIdx.x] += sm[threadIdx.x + off];\n __syncthreads();\n }\n if (threadIdx.x == 0) {\n float y = (sm[0] - brm[k]) * rsqrtf(brv[k] + eps) * bw[k] + bb[k];\n h[n * K + k] = y > 0.0f ? y : 0.0f;\n }\n}\n\n__global__ void fc_kernel(const float* __restrict__ h,\n const float* __restrict__ fw,\n const float* __restrict__ fb,\n float* __restrict__ out,\n int N, int K, int O) {\n int idx = blockIdx.x;\n int o = idx % O;\n int n = idx / O;\n const float* hp = h + n * K;\n const float* wp = fw + o * K;\n float sum = 0.0f;\n for (int k = threadIdx.x; k < K; k += blockDim.x) {\n sum += hp[k] * wp[k];\n }\n __shared__ float sm[256];\n sm[threadIdx.x] = sum;\n __syncthreads();\n for (int off = blockDim.x >> 1; off > 0; off >>= 1) {\n if (threadIdx.x < off) sm[threadIdx.x] += sm[threadIdx.x + off];\n __syncthreads();\n }\n if (threadIdx.x == 0) out[n * O + o] = sm[0] + fb[o];\n}\n\ntorch::Tensor eff_tail_forward(torch::Tensor x,\n torch::Tensor conv_w,\n torch::Tensor bn_w,\n torch::Tensor bn_b,\n torch::Tensor bn_rm,\n torch::Tensor bn_rv,\n torch::Tensor fc_w,\n torch::Tensor fc_b,\n double eps) {\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 K = (int)conv_w.size(0);\n const int O = (int)fc_w.size(0);\n auto opts = x.options();\n auto means = torch::empty({N, C}, opts);\n auto hidden = torch::empty({N, K}, opts);\n auto out = torch::empty({N, O}, opts);\n cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n spatial_mean_kernel<<<N * C, 128, 0, stream>>>(x.data_ptr<float>(), means.data_ptr<float>(), N, C, H, W);\n hidden_kernel<<<N * K, 256, 0, stream>>>(means.data_ptr<float>(), conv_w.data_ptr<float>(),\n bn_w.data_ptr<float>(), bn_b.data_ptr<float>(),\n bn_rm.data_ptr<float>(), bn_rv.data_ptr<float>(),\n hidden.data_ptr<float>(), N, C, K, (float)eps);\n fc_kernel<<<N * O, 256, 0, stream>>>(hidden.data_ptr<float>(), fc_w.data_ptr<float>(),\n fc_b.data_ptr<float>(), out.data_ptr<float>(), N, K, O);\n return out;\n}\n'''\n\n_cpp_src = r'''\n#include <torch/extension.h>\ntorch::Tensor eff_tail_forward(torch::Tensor x,\n torch::Tensor conv_w,\n torch::Tensor bn_w,\n torch::Tensor bn_b,\n torch::Tensor bn_rm,\n torch::Tensor bn_rv,\n torch::Tensor fc_w,\n torch::Tensor fc_b,\n double eps);\n'''\n\n_eff_ext = load_inline(\n name='effnet_b1_tail_ext_v1',\n cpp_sources=_cpp_src,\n cuda_sources=_cuda_src,\n functions=['eff_tail_forward'],\n extra_cuda_cflags=['-O3', '--use_fast_math'],\n extra_cflags=['-O3'],\n verbose=False,\n)\n\nclass ModelNew(nn.Module):\n def __init__(self, num_classes=1000):\n super(ModelNew, self).__init__()\n self.conv1 = nn.Conv2d(3, 32, kernel_size=3, stride=2, padding=1, bias=False)\n self.bn1 = nn.BatchNorm2d(32)\n self.mbconv1 = self._make_mbconv_block(32, 16, 1, 1)\n self.mbconv2 = self._make_mbconv_block(16, 24, 2, 6)\n self.mbconv3 = self._make_mbconv_block(24, 40, 2, 6)\n self.mbconv4 = self._make_mbconv_block(40, 80, 2, 6)\n self.mbconv5 = self._make_mbconv_block(80, 112, 1, 6)\n self.mbconv6 = self._make_mbconv_block(112, 192, 2, 6)\n self.mbconv7 = self._make_mbconv_block(192, 320, 1, 6)\n self.conv2 = nn.Conv2d(320, 1280, kernel_size=1, stride=1, padding=0, bias=False)\n self.bn2 = nn.BatchNorm2d(1280)\n self.fc = nn.Linear(1280, num_classes)\n\n def _make_mbconv_block(self, in_channels, out_channels, stride, expand_ratio):\n hidden_dim = round(in_channels * expand_ratio)\n return nn.Sequential(\n nn.Conv2d(in_channels, hidden_dim, kernel_size=1, stride=1, padding=0, bias=False),\n nn.BatchNorm2d(hidden_dim),\n nn.ReLU6(inplace=True),\n nn.Conv2d(hidden_dim, hidden_dim, kernel_size=3, stride=stride, padding=1, groups=hidden_dim, bias=False),\n nn.BatchNorm2d(hidden_dim),\n nn.ReLU6(inplace=True),\n nn.Conv2d(hidden_dim, out_channels, kernel_size=1, stride=1, padding=0, bias=False),\n nn.BatchNorm2d(out_channels),\n )\n\n def forward(self, x):\n x = F.relu(self.bn1(self.conv1(x)))\n x = self.mbconv1(x)\n x = self.mbconv2(x)\n x = self.mbconv3(x)\n x = self.mbconv4(x)\n x = self.mbconv5(x)\n x = self.mbconv6(x)\n x = self.mbconv7(x)\n # In inference mode, commute the final 1x1 convolution with global average pooling\n # and fuse conv2 + bn2 + relu + avgpool + linear in custom CUDA kernels.\n if x.is_cuda and x.dtype is torch.float32 and (not self.training):\n return _eff_ext.eff_tail_forward(\n x.contiguous(), self.conv2.weight.contiguous().view(1280, 320),\n self.bn2.weight.contiguous(), self.bn2.bias.contiguous(),\n self.bn2.running_mean.contiguous(), self.bn2.running_var.contiguous(),\n self.fc.weight.contiguous(), self.fc.bias.contiguous(), float(self.bn2.eps))\n else:\n x = F.relu(self.bn2(self.conv2(x)))\n x = F.adaptive_avg_pool2d(x, (1, 1))\n x = torch.flatten(x, 1)\n x = self.fc(x)\n return x\n" }
result ← submit_kernel (fail)
failsubmit_kernel submit_kernel FAILED: kernel did not compile. Error building extension 'effnet_b1_tail_ext_v1': [1/3] /opt/conda/bin/x86_64-conda-linux-gnu-c++ -MMD -MF main.o.d -DTORCH_EXTENSION_NAME=effnet_b1_tail_ext_v1 -DTORCH_API_INCLUDE_EXTENSION_H -isystem /scratch/abaner/KernelBench/.venv/lib/python3.10/site-packages/torch/include -isystem /scratch/abaner/KernelBench/.venv/lib/python3.10/site-packages/torch/include/torch/csrc/api/include -isystem /usr/local/cuda/include -isystem /opt/conda/include/python3.10 -fPIC -std=c++17 -O3 -c /scratch/abaner/KernelBench/runs/l123_single_gpt/original/gpt-5.5-priority/level_3_problem_23_cache/k_c2a304d85ffd/effnet_b1_tail_ext_v1/main.cpp -o main.o [2/3] /usr/local/cuda/bin/nvcc -MD -MF cuda.cuda.o.d -ccbin /opt/conda/bin/x86_64-conda-linux-gnu-cc -DTORCH_EXTENSION_NAME=effnet_b1_tail_ext_v1 -DTORCH_API_INCLUDE_EXTENSION_H -isystem /scratch/abaner/KernelBench/.venv/lib/python3.10/site-packages/torch/include -isystem /scratch/abaner/KernelBench/.venv/lib/python3.10/site-packages/torch/include/torch/csrc/api/include -isystem /usr/local/cuda/include -isystem /opt/conda/include/python3.10 -D__CUDA_NO_HALF_OPERATORS__ -D__CUDA_NO_HALF_CONVERSIONS__ -D__CUDA_NO_BFLOAT16_CONVERSIONS__ -D__CUDA_NO_HALF2_OPERATORS__ --expt-relaxed-constexpr -gencode=arch=compute_90,code=compute_90 -gencode=arch=compute_90,code=sm_90 --compiler-options '-fPIC' -O3 --use_fast_math -std=c++17 -c /scratch/abaner/KernelBench/runs/l123_single_gpt/original/gpt-5.5-priority/level_3_problem_23_cache/k_c2a304d85ffd/effnet_b1_tail_ext_v1/cuda.cu -o cuda.cuda.o FAILED: [code=2] cuda.cuda.o /usr/local/cuda/bin/nvcc -MD -MF cuda.cuda.o.d -ccbin /opt/conda/bin/x86_64-conda-linux-gnu-cc -DTORCH_EXTENSION_NAME=effnet_b1_tail_ext_v1 -DTORCH_API_INCLUDE_EXTENSION_H -isystem /scratch/abaner/KernelBench/.venv/lib/python3.10/site-packages/torch/include -isystem /scratch/abaner/KernelBench/.venv/lib/python3.10/site-packages/torch/include/torch/csrc/api/include -isystem /usr/local/cuda/include -isystem /opt/conda/include/python3.10 -D__CUDA_NO_HALF_OPERATORS__ -D__CUDA_NO_HALF_CONVERSIONS__ -D__CUDA_NO_BFLOAT16_CONVERSIONS__ -D__CUDA_NO_HALF2_OPERATORS__ --expt-relaxed-constexpr -gencode=arch=compute_90,code=compute_90 -gencode=arch=compute_90,code=sm_90 --compiler-options '-fPIC' -O3 --use_fast_math -std=c++17 -c /scratch/abaner/KernelBench/runs/l123_single_gpt/original/gpt-5.5-priority/level_3_problem_23_cache/k_c2a304d85ffd/effnet_b1_tail_ext_v1/cuda.cu -o cuda.cuda.o nvcc warning : incompatible redefinition for option 'compiler-bindir', the last value of this option was used /scratch/abaner/KernelBench/runs/l123_single_gpt/original/gpt-5.5-priority/level_3_problem_23_cache/k_c2a304d85ffd/effnet_b1_tail_ext_v1/cuda.cu(103): error: namespace "at::cuda" has no member "getCurrentCUDAStream" cudaStream_t stream = at::cuda::getCurrentCUDAStream(); ^ 1 error detected in the compilation of "/scratch/abaner/KernelBench/runs/l123_single_gpt/original/gpt-5.5-priority/level_3_problem_23_cache/k_c2a304d85ffd/effnet_b1_tail_ext_v1/cuda.cu". ninja: build stopped: subcommand failed.
final submitted kernel (192 lines, 7,844 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

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

__global__ void spatial_mean_kernel(const float* __restrict__ x, float* __restrict__ m,
                                    int N, int C, int H, int W) {
    int idx = blockIdx.x;
    int c = idx % C;
    int n = idx / C;
    int HW = H * W;
    const float* p = x + ((n * C + c) * H * W);
    float s = 0.0f;
    // H*W is normally 64 here; keep generic.
    for (int i = threadIdx.x; i < HW; i += blockDim.x) s += p[i];
    __shared__ float sm[128];
    sm[threadIdx.x] = s;
    __syncthreads();
    for (int off = blockDim.x >> 1; off > 0; off >>= 1) {
        if (threadIdx.x < off) sm[threadIdx.x] += sm[threadIdx.x + off];
        __syncthreads();
    }
    if (threadIdx.x == 0) m[n * C + c] = sm[0] / (float)HW;
}

__global__ void hidden_kernel(const float* __restrict__ m,
                              const float* __restrict__ cw,
                              const float* __restrict__ bw,
                              const float* __restrict__ bb,
                              const float* __restrict__ brm,
                              const float* __restrict__ brv,
                              float* __restrict__ h,
                              int N, int C, int K, float eps) {
    int idx = blockIdx.x;
    int k = idx % K;
    int n = idx / K;
    float sum = 0.0f;
    const float* mp = m + n * C;
    const float* wp = cw + k * C;
    for (int c = threadIdx.x; c < C; c += blockDim.x) {
        sum += mp[c] * wp[c];
    }
    __shared__ float sm[256];
    sm[threadIdx.x] = sum;
    __syncthreads();
    for (int off = blockDim.x >> 1; off > 0; off >>= 1) {
        if (threadIdx.x < off) sm[threadIdx.x] += sm[threadIdx.x + off];
        __syncthreads();
    }
    if (threadIdx.x == 0) {
        float y = (sm[0] - brm[k]) * rsqrtf(brv[k] + eps) * bw[k] + bb[k];
        h[n * K + k] = y > 0.0f ? y : 0.0f;
    }
}

__global__ void fc_kernel(const float* __restrict__ h,
                          const float* __restrict__ fw,
                          const float* __restrict__ fb,
                          float* __restrict__ out,
                          int N, int K, int O) {
    int idx = blockIdx.x;
    int o = idx % O;
    int n = idx / O;
    const float* hp = h + n * K;
    const float* wp = fw + o * K;
    float sum = 0.0f;
    for (int k = threadIdx.x; k < K; k += blockDim.x) {
        sum += hp[k] * wp[k];
    }
    __shared__ float sm[256];
    sm[threadIdx.x] = sum;
    __syncthreads();
    for (int off = blockDim.x >> 1; off > 0; off >>= 1) {
        if (threadIdx.x < off) sm[threadIdx.x] += sm[threadIdx.x + off];
        __syncthreads();
    }
    if (threadIdx.x == 0) out[n * O + o] = sm[0] + fb[o];
}

torch::Tensor eff_tail_forward(torch::Tensor x,
                               torch::Tensor conv_w,
                               torch::Tensor bn_w,
                               torch::Tensor bn_b,
                               torch::Tensor bn_rm,
                               torch::Tensor bn_rv,
                               torch::Tensor fc_w,
                               torch::Tensor fc_b,
                               double eps) {
    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 K = (int)conv_w.size(0);
    const int O = (int)fc_w.size(0);
    auto opts = x.options();
    auto means = torch::empty({N, C}, opts);
    auto hidden = torch::empty({N, K}, opts);
    auto out = torch::empty({N, O}, opts);
    cudaStream_t stream = at::cuda::getCurrentCUDAStream();
    spatial_mean_kernel<<<N * C, 128, 0, stream>>>(x.data_ptr<float>(), means.data_ptr<float>(), N, C, H, W);
    hidden_kernel<<<N * K, 256, 0, stream>>>(means.data_ptr<float>(), conv_w.data_ptr<float>(),
                                             bn_w.data_ptr<float>(), bn_b.data_ptr<float>(),
                                             bn_rm.data_ptr<float>(), bn_rv.data_ptr<float>(),
                                             hidden.data_ptr<float>(), N, C, K, (float)eps);
    fc_kernel<<<N * O, 256, 0, stream>>>(hidden.data_ptr<float>(), fc_w.data_ptr<float>(),
                                         fc_b.data_ptr<float>(), out.data_ptr<float>(), N, K, O);
    return out;
}
'''

_cpp_src = r'''
#include <torch/extension.h>
torch::Tensor eff_tail_forward(torch::Tensor x,
                               torch::Tensor conv_w,
                               torch::Tensor bn_w,
                               torch::Tensor bn_b,
                               torch::Tensor bn_rm,
                               torch::Tensor bn_rv,
                               torch::Tensor fc_w,
                               torch::Tensor fc_b,
                               double eps);
'''

_eff_ext = load_inline(
    name='effnet_b1_tail_ext_v1',
    cpp_sources=_cpp_src,
    cuda_sources=_cuda_src,
    functions=['eff_tail_forward'],
    extra_cuda_cflags=['-O3', '--use_fast_math'],
    extra_cflags=['-O3'],
    verbose=False,
)

class ModelNew(nn.Module):
    def __init__(self, num_classes=1000):
        super(ModelNew, self).__init__()
        self.conv1 = nn.Conv2d(3, 32, kernel_size=3, stride=2, padding=1, bias=False)
        self.bn1 = nn.BatchNorm2d(32)
        self.mbconv1 = self._make_mbconv_block(32, 16, 1, 1)
        self.mbconv2 = self._make_mbconv_block(16, 24, 2, 6)
        self.mbconv3 = self._make_mbconv_block(24, 40, 2, 6)
        self.mbconv4 = self._make_mbconv_block(40, 80, 2, 6)
        self.mbconv5 = self._make_mbconv_block(80, 112, 1, 6)
        self.mbconv6 = self._make_mbconv_block(112, 192, 2, 6)
        self.mbconv7 = self._make_mbconv_block(192, 320, 1, 6)
        self.conv2 = nn.Conv2d(320, 1280, kernel_size=1, stride=1, padding=0, bias=False)
        self.bn2 = nn.BatchNorm2d(1280)
        self.fc = nn.Linear(1280, num_classes)

    def _make_mbconv_block(self, in_channels, out_channels, stride, expand_ratio):
        hidden_dim = round(in_channels * expand_ratio)
        return nn.Sequential(
            nn.Conv2d(in_channels, hidden_dim, kernel_size=1, stride=1, padding=0, bias=False),
            nn.BatchNorm2d(hidden_dim),
            nn.ReLU6(inplace=True),
            nn.Conv2d(hidden_dim, hidden_dim, kernel_size=3, stride=stride, padding=1, groups=hidden_dim, bias=False),
            nn.BatchNorm2d(hidden_dim),
            nn.ReLU6(inplace=True),
            nn.Conv2d(hidden_dim, out_channels, kernel_size=1, stride=1, padding=0, bias=False),
            nn.BatchNorm2d(out_channels),
        )

    def forward(self, x):
        x = F.relu(self.bn1(self.conv1(x)))
        x = self.mbconv1(x)
        x = self.mbconv2(x)
        x = self.mbconv3(x)
        x = self.mbconv4(x)
        x = self.mbconv5(x)
        x = self.mbconv6(x)
        x = self.mbconv7(x)
        # In inference mode, commute the final 1x1 convolution with global average pooling
        # and fuse conv2 + bn2 + relu + avgpool + linear in custom CUDA kernels.
        if x.is_cuda and x.dtype is torch.float32 and (not self.training):
            return _eff_ext.eff_tail_forward(
                x.contiguous(), self.conv2.weight.contiguous().view(1280, 320),
                self.bn2.weight.contiguous(), self.bn2.bias.contiguous(),
                self.bn2.running_mean.contiguous(), self.bn2.running_var.contiguous(),
                self.fc.weight.contiguous(), self.fc.bias.contiguous(), float(self.bn2.eps))
        else:
            x = F.relu(self.bn2(self.conv2(x)))
            x = F.adaptive_avg_pool2d(x, (1, 1))
            x = torch.flatten(x, 1)
            x = self.fc(x)
            return x