grok-4.3 · popcorn · pop_l123_st_gpt
correct 28_FusedGroupNormSiLU.py
model
grok-4.3
turns / max
1 / 1
tool calls
1
tokens (total)
2919
wall clock
275.2s
truncated
no
backend
cuda fp32
variant
popcorn
runtime
0.02 μs
ref runtime
0.03 μs
speedup
1.38x
started
2026-05-11T09:40:29Z
timingkernelbench.timing
kernel runtime
0.02 μs
ref runtime
0.03 μs
speedup
1.38x
kernel mean / median / std
0.02 / — / 2.25e-04 μs
ref mean / median / std
0.03 / — / 0.02 μs
numerical precisioneval_kernel_against_ref
max abs error
1.43e-06
mean abs error
2.37e-08
max rel error
0.01
mean rel error
7.42e-08
gpu memoryextended_metrics.measure_memory
kernel peak
9.00 MB
ref peak
13.50 MB
kernel / ref
0.67x
kernel launches / fusiontorch.profiler
kernel launches
2
ref launches
12
fusion ratio (ref / kernel)
6.00x
total cuda time
93 μs
energyNVML (pynvml)
kernel mJ / run
242.92
ref mJ / run
0.00
ratio (ref / kernel)
—
avg power
16496.56 W
ref avg power
0.00 W
SOL (speed-of-light)nsight_hardware_counters
SOL score (0–1)
0.164
dram util %
7.6%
compute util %
16.4%
bottleneck
compute-bound
dominant pipe
FP32
arithmetic intensity
—
ridge point
20.00
roofline / occupancynsight_hardware_counters
dram bandwidth
253.65 GB/s
dram util %
7.6%
fp32 tflops
—
fp32 util %
—
fp16 tflops
—
occupancy %
23.9%
bottleneck
compute-bound
dominant pipe %
16.4%
arithmetic intensity
—
ridge point
20.00
L1 hit %
—
L2 hit %
56.7%
ld sectors / req
2.80
st sectors / req
—
regs / thread
32
smem / block
2048 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
572.0%
short_scoreboard
85.3%
mio_throttle
3.7%
reference PyTorch source (the prompt the agent sees)
import torch
import torch.nn as nn
import torch.nn.functional as F

# Default ModelNew is PyTorch-only. run_and_check with check_kernel=True and backend=cuda
# requires real CUDA (__global__ + load_inline/cpp_extension) in the kernel file—use
# check_kernel=False until you add a custom kernel implementation.

class Model(nn.Module):
    """
    Fused GroupNorm + SiLU (Swish), a common post-conv / residual-branch pattern in
    ResNet-family backbones (SiLU instead of ReLU) and in spatial blocks of diffusion
    transformers (DiT) where features are (N, C, H, W).

    Reference pipeline for a single fused kernel: normalize channels with
    `GroupNorm`, then apply `SiLU` elementwise (`x * sigmoid(x)`).
    """

    def __init__(
        self,
        num_channels: int,
        num_groups: int,
        eps: float = 1e-5,
        affine: bool = True,
    ):
        super().__init__()
        assert num_channels % num_groups == 0
        self.norm = nn.GroupNorm(
            num_groups=num_groups,
            num_channels=num_channels,
            eps=eps,
            affine=affine,
        )

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        """
        Args:
            x: (N, C, H, W) feature map
        Returns:
            Same shape as x
        """
        return F.silu(self.norm(x))


class ModelNew(nn.Module):
    """
    KernelBench candidate entry point. Replace with a fused CUDA/Triton kernel;
    default delegates to `Model` for harness smoke tests (same file as ref + kernel).
    """

    def __init__(
        self,
        num_channels: int,
        num_groups: int,
        eps: float = 1e-5,
        affine: bool = True,
    ):
        super().__init__()
        self._impl = Model(num_channels, num_groups, eps=eps, affine=affine)

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        return self._impl(x)


batch_size = 8
num_channels = 256
num_groups = 32
height = 24
width = 24


def get_inputs():
    p = popcorn_pri
    x = torch.randn(p.jitter_int(batch_size), num_channels, p.jitter_int(height), p.jitter_int(width))
    return [x]


def get_init_inputs():
    return [num_channels, num_groups]
turn 0275.2s turn · 25.1s LLM · 250.1s tools · tokens in=1,383 · out=1,231 · total=2,919 · FINAL
reasoning
The task is to replace the forward computation of a PyTorch model with a custom CUDA kernel. The model is GroupNorm followed by SiLU activation.
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\ncuda_source = r'''\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n\n__global__ void fused_groupnorm_silu_kernel(\n const float* __restrict__ input,\n float* __restrict__ output,\n const float* __restrict__ gamma,\n const float* __restrict__ beta,\n int N, int C, int H, int W, int G, int C_per_G, float eps) {\n \n // Each block handles one (n, g) pair\n int ng = blockIdx.x;\n int n = ng / G;\n int g = ng % G;\n \n int HW = H * W;\n int group_size = C_per_G * HW;\n \n extern __shared__ float sdata[];\n \n // Compute mean\n float sum = 0.0f;\n for (int i = threadIdx.x; i < group_size; i += blockDim.x) {\n int c_in_g = i / HW;\n int hw = i % HW;\n int c = g * C_per_G + c_in_g;\n int idx = ((n * C + c) * HW) + hw;\n sum += input[idx];\n }\n \n sdata[threadIdx.x] = sum;\n __syncthreads();\n \n for (int s = blockDim.x / 2; s > 0; s >>= 1) {\n if (threadIdx.x < s) sdata[threadIdx.x] += sdata[threadIdx.x + s];\n __syncthreads();\n }\n float mean = sdata[0] / group_size;\n \n // Compute variance\n float var_sum = 0.0f;\n for (int i = threadIdx.x; i < group_size; i += blockDim.x) {\n int c_in_g = i / HW;\n int hw = i % HW;\n int c = g * C_per_G + c_in_g;\n int idx = ((n * C + c) * HW) + hw;\n float val = input[idx] - mean;\n var_sum += val * val;\n }\n \n sdata[threadIdx.x] = var_sum;\n __syncthreads();\n \n for (int s = blockDim.x / 2; s > 0; s >>= 1) {\n if (threadIdx.x < s) sdata[threadIdx.x] += sdata[threadIdx.x + s];\n __syncthreads();\n }\n float var = sdata[0] / group_size;\n float inv_std = rsqrtf(var + eps);\n \n // Normalize, apply affine and SiLU\n for (int i = threadIdx.x; i < group_size; i += blockDim.x) {\n int c_in_g = i / HW;\n int hw = i % HW;\n int c = g * C_per_G + c_in_g;\n int idx = ((n * C + c) * HW) + hw;\n float val = input[idx];\n float norm = (val - mean) * inv_std;\n if (gamma != nullptr && beta != nullptr) {\n norm = norm * gamma[c] + beta[c];\n }\n float silu = norm * (1.0f / (1.0f + expf(-norm)));\n output[idx] = silu;\n }\n}\n\ntorch::Tensor fused_groupnorm_silu_cuda(\n torch::Tensor input,\n torch::Tensor gamma,\n torch::Tensor beta,\n int num_groups,\n float eps) {\n \n auto N = input.size(0);\n auto C = input.size(1);\n auto H = input.size(2);\n auto W = input.size(3);\n int G = num_groups;\n int C_per_G = C / G;\n \n auto output = torch::empty_like(input);\n \n int num_blocks = N * G;\n int threads = 256;\n \n fused_groupnorm_silu_kernel<<<num_blocks, threads, threads * sizeof(float)>>>(\n input.data_ptr<float>(),\n output.data_ptr<float>(),\n gamma.defined() ? gamma.data_ptr<float>() : nullptr,\n beta.defined() ? beta.data_ptr<float>() : nullptr,\n N, C, H, W, G, C_per_G, eps\n );\n \n return output;\n}\n'''\n\ncpp_source = r'''\ntorch::Tensor fused_groupnorm_silu_cuda(\n torch::Tensor input,\n torch::Tensor gamma,\n torch::Tensor beta,\n int num_groups,\n float eps);\n'''\n\nmodule = load_inline(\n name='fused_gn_silu',\n cpp_sources=cpp_source,\n cuda_sources=cuda_source,\n functions=['fused_groupnorm_silu_cuda'],\n verbose=False\n)\n\nclass ModelNew(nn.Module):\n def __init__(self, num_channels: int, num_groups: int, eps: float = 1e-5, affine: bool = True):\n super().__init__()\n self.num_groups = num_groups\n self.eps = eps\n self.affine = affine\n self.weight = nn.Parameter(torch.ones(num_channels)) if affine else None\n self.bias = nn.Parameter(torch.zeros(num_channels)) if affine else None\n \n def forward(self, x: torch.Tensor) -> torch.Tensor:\n gamma = self.weight if self.weight is not None else torch.empty(0, device=x.device)\n beta = self.bias if self.bias is not None else torch.empty(0, device=x.device)\n return module.fused_groupnorm_silu_cuda(x, gamma, beta, self.num_groups, self.eps)\n" }
result ← submit_kernel (ok)
oksubmit_kernel submit_kernel PASSED: (5 / 5) correctness trials all passed. Kernel runtime: 0.02 μs Runtime stats: mean=0.02μs median=?μs std=0.00μs Numerical precision: max_abs_err=1.43e-06 mean_abs_err=2.37e-08 max_rel_err=1.25e-02 Memory: 9.0 MB peak (ref: 13.5 MB, ratio: 0.6668x) Kernel launches: 2 (ref: 12, fusion ratio: 6.0) Energy: 242.92 mJ/run (ref: 0.0 mJ, ratio: -1x) SOL score: 0.1637
final submitted kernel (145 lines, 4,223 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_source = r'''
#include <torch/extension.h>
#include <cuda.h>
#include <cuda_runtime.h>

__global__ void fused_groupnorm_silu_kernel(
    const float* __restrict__ input,
    float* __restrict__ output,
    const float* __restrict__ gamma,
    const float* __restrict__ beta,
    int N, int C, int H, int W, int G, int C_per_G, float eps) {
    
    // Each block handles one (n, g) pair
    int ng = blockIdx.x;
    int n = ng / G;
    int g = ng % G;
    
    int HW = H * W;
    int group_size = C_per_G * HW;
    
    extern __shared__ float sdata[];
    
    // Compute mean
    float sum = 0.0f;
    for (int i = threadIdx.x; i < group_size; i += blockDim.x) {
        int c_in_g = i / HW;
        int hw = i % HW;
        int c = g * C_per_G + c_in_g;
        int idx = ((n * C + c) * HW) + hw;
        sum += input[idx];
    }
    
    sdata[threadIdx.x] = sum;
    __syncthreads();
    
    for (int s = blockDim.x / 2; s > 0; s >>= 1) {
        if (threadIdx.x < s) sdata[threadIdx.x] += sdata[threadIdx.x + s];
        __syncthreads();
    }
    float mean = sdata[0] / group_size;
    
    // Compute variance
    float var_sum = 0.0f;
    for (int i = threadIdx.x; i < group_size; i += blockDim.x) {
        int c_in_g = i / HW;
        int hw = i % HW;
        int c = g * C_per_G + c_in_g;
        int idx = ((n * C + c) * HW) + hw;
        float val = input[idx] - mean;
        var_sum += val * val;
    }
    
    sdata[threadIdx.x] = var_sum;
    __syncthreads();
    
    for (int s = blockDim.x / 2; s > 0; s >>= 1) {
        if (threadIdx.x < s) sdata[threadIdx.x] += sdata[threadIdx.x + s];
        __syncthreads();
    }
    float var = sdata[0] / group_size;
    float inv_std = rsqrtf(var + eps);
    
    // Normalize, apply affine and SiLU
    for (int i = threadIdx.x; i < group_size; i += blockDim.x) {
        int c_in_g = i / HW;
        int hw = i % HW;
        int c = g * C_per_G + c_in_g;
        int idx = ((n * C + c) * HW) + hw;
        float val = input[idx];
        float norm = (val - mean) * inv_std;
        if (gamma != nullptr && beta != nullptr) {
            norm = norm * gamma[c] + beta[c];
        }
        float silu = norm * (1.0f / (1.0f + expf(-norm)));
        output[idx] = silu;
    }
}

torch::Tensor fused_groupnorm_silu_cuda(
    torch::Tensor input,
    torch::Tensor gamma,
    torch::Tensor beta,
    int num_groups,
    float eps) {
    
    auto N = input.size(0);
    auto C = input.size(1);
    auto H = input.size(2);
    auto W = input.size(3);
    int G = num_groups;
    int C_per_G = C / G;
    
    auto output = torch::empty_like(input);
    
    int num_blocks = N * G;
    int threads = 256;
    
    fused_groupnorm_silu_kernel<<<num_blocks, threads, threads * sizeof(float)>>>(
        input.data_ptr<float>(),
        output.data_ptr<float>(),
        gamma.defined() ? gamma.data_ptr<float>() : nullptr,
        beta.defined() ? beta.data_ptr<float>() : nullptr,
        N, C, H, W, G, C_per_G, eps
    );
    
    return output;
}
'''

cpp_source = r'''
torch::Tensor fused_groupnorm_silu_cuda(
    torch::Tensor input,
    torch::Tensor gamma,
    torch::Tensor beta,
    int num_groups,
    float eps);
'''

module = load_inline(
    name='fused_gn_silu',
    cpp_sources=cpp_source,
    cuda_sources=cuda_source,
    functions=['fused_groupnorm_silu_cuda'],
    verbose=False
)

class ModelNew(nn.Module):
    def __init__(self, num_channels: int, num_groups: int, eps: float = 1e-5, affine: bool = True):
        super().__init__()
        self.num_groups = num_groups
        self.eps = eps
        self.affine = affine
        self.weight = nn.Parameter(torch.ones(num_channels)) if affine else None
        self.bias = nn.Parameter(torch.zeros(num_channels)) if affine else None
    
    def forward(self, x: torch.Tensor) -> torch.Tensor:
        gamma = self.weight if self.weight is not None else torch.empty(0, device=x.device)
        beta = self.bias if self.bias is not None else torch.empty(0, device=x.device)
        return module.fused_groupnorm_silu_cuda(x, gamma, beta, self.num_groups, self.eps)