FW-GLM-5-1 · popcorn · pop_l123_default_gpt
correct 28_FusedGroupNormSiLU.py
model
FW-GLM-5-1
turns / max
5 / 12
tool calls
5
tokens (total)
25942
wall clock
1859.2s
truncated
no
backend
cuda fp32
variant
popcorn
runtime
0.01 μs
ref runtime
0.03 μs
speedup
1.89x
started
2026-05-11T12:17:29Z
timingkernelbench.timing
kernel runtime
0.01 μs
ref runtime
0.03 μs
speedup
1.89x
kernel mean / median / std
0.01 / — / 0.00 μs
ref mean / median / std
0.03 / — / 0.00 μs
numerical precisioneval_kernel_against_ref
max abs error
1.43e-06
mean abs error
2.54e-08
max rel error
0.01
mean rel error
7.49e-08
gpu memoryextended_metrics.measure_memory
kernel peak
42.00 MB
ref peak
46.50 MB
kernel / ref
0.90x
kernel launches / fusiontorch.profiler
kernel launches
2
ref launches
12
fusion ratio (ref / kernel)
6.00x
total cuda time
11 μs
SOL (speed-of-light)nsight_hardware_counters
SOL score (0–1)
0.220
dram util %
22.0%
compute util %
14.0%
bottleneck
memory-bound
dominant pipe
FP32
arithmetic intensity
—
ridge point
20.00
roofline / occupancynsight_hardware_counters
dram bandwidth
736.24 GB/s
dram util %
22.0%
fp32 tflops
—
fp32 util %
—
fp16 tflops
—
occupancy %
23.6%
bottleneck
memory-bound
dominant pipe %
14.0%
arithmetic intensity
—
ridge point
20.00
L1 hit %
—
L2 hit %
54.8%
ld sectors / req
2.50
st sectors / req
—
regs / thread
32
smem / block
3072 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
385.9%
short_scoreboard
125.3%
mio_throttle
13.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 0358.4s turn · 3.4s LLM · 355.0s tools · tokens in=1,755 · out=248 · total=2,003
reasoning
Let me analyze this task. I need to create a custom CUDA kernel that fuses GroupNorm + SiLU (Swish) activation. The reference implementation: 1. Applies GroupNorm to normalize channels 2. Applies SiLU activation (x * sigmoid(x)) GroupNorm operation: - Divides channels into groups - Normalizes each group independently - If affine=True, applies scale (gamma) and shift (beta) parameters For a tensor of shape (N, C, H, W) with num_groups: - Each sample has C channels divided into num_groups groups - Each group has C/num_groups channels - For each (n, g) pair, we normalize over (C/num_groups * H * W) elements The normalization formula: mean = mean of elements in group var = variance of elements in group x_norm = (x - mean) / sqrt(var + eps) if affine: output = gamma * x_norm + beta Then SiLU: output = x_norm * sigmoid(x_norm) Let me first get GPU specs to understand the hardware constraints.
assistantI'll start by analyzing this task and getting GPU specs to understand the hardware constraints.
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) · 621.5s turn · 17.9s LLM · tokens in=2,020 · out=1,787 · total=3,807 · 8,238 chars (click to expand)
turn 1621.5s turn · 17.9s LLM · 603.6s tools · tokens in=2,020 · out=1,787 · total=3,807
reasoning
Now I understand the GPU. Let me write a fused GroupNorm + SiLU kernel. The key operations are: 1. GroupNorm: Normalize each channel group, then optionally apply affine transformation (weight * x + bias) 2. SiLU: x * sigmoid(x) = x / (1 + exp(-x)) For GroupNorm with G groups and C channels: - Each group has C/G channels - For each sample n and each group g, we compute: - mean = (1/(C/G * H * W)) * sum over channels in group and spatial dims - var = (1/(C/G * H * W)) * sum of (x - mean)^2 - x_norm = (x - mean) / sqrt(var + eps) - If affine: y = weight[c] * x_norm + bias[c] Let me write an efficient kernel. The typical approach is: - Each thread block handles one (batch, group) pair - Use parallel reduction for mean/var computation - Use shared memory for intermediate results Let me write the kernel:
assistantNow I'll write a fused GroupNorm + SiLU CUDA kernel. The key is to efficiently compute the group normalization statistics and apply the SiLU activation in a single pass.
call → compile_kernel
callcompile_kernel { "kernel_code": "import torch\nimport torch.nn as nn\nimport torch.utils.cpp_extension as cpp_extension\n\nCUDA_SOURCE = r\"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <cmath>\n\n// Fused GroupNorm + SiLU kernel\n// Each thread block processes one (batch, group) pair\n// Threads within block cooperatively compute mean, variance, then apply normalization + SiLU\n\ntemplate <int BLOCK_SIZE>\n__global__ void fused_groupnorm_silu_kernel(\n const float* __restrict__ input,\n float* __restrict__ output,\n const float* __restrict__ weight,\n const float* __restrict__ bias,\n int N, int C, int H, int W, int G, int channels_per_group, float eps\n) {\n // Each block handles one (batch_item, group) pair\n int batch_idx = blockIdx.x / G;\n int group_idx = blockIdx.x % G;\n \n int group_start_channel = group_idx * channels_per_group;\n int group_size = channels_per_group * H * W;\n \n // Index for this thread's element within the group\n int tid = threadIdx.x;\n int total_elements = group_size;\n \n // Pointers to the start of this batch and group in input/output\n int batch_offset = batch_idx * C * H * W;\n int group_offset = group_start_channel * H * W;\n const float* __restrict__ input_ptr = input + batch_offset + group_offset;\n float* __restrict__ output_ptr = output + batch_offset + group_offset;\n \n // Shared memory for reduction\n __shared__ float s_mean[BLOCK_SIZE];\n __shared__ float s_var[BLOCK_SIZE];\n \n // Compute mean using parallel reduction\n float local_sum = 0.0f;\n float local_sq_sum = 0.0f;\n \n // Grid-stride loop to process all elements in the group\n for (int i = tid; i < total_elements; i += BLOCK_SIZE) {\n float val = input_ptr[i];\n local_sum += val;\n local_sq_sum += val * val;\n }\n \n s_mean[tid] = local_sum;\n s_var[tid] = local_sq_sum;\n __syncthreads();\n \n // Parallel reduction in shared memory\n for (int stride = BLOCK_SIZE / 2; stride > 0; stride >>= 1) {\n if (tid < stride) {\n s_mean[tid] += s_mean[tid + stride];\n s_var[tid] += s_var[tid + stride];\n }\n __syncthreads();\n }\n \n // Compute mean and variance\n float mean = 0.0f;\n float inv_std = 0.0f;\n if (tid == 0) {\n mean = s_mean[0] / total_elements;\n float variance = s_var[0] / total_elements - mean * mean;\n inv_std = 1.0f / sqrtf(variance + eps);\n s_mean[0] = mean;\n s_var[0] = inv_std;\n }\n __syncthreads();\n \n mean = s_mean[0];\n inv_std = s_var[0];\n \n // Apply normalization, affine, and SiLU\n for (int i = tid; i < total_elements; i += BLOCK_SIZE) {\n int c_local = i / (H * W);\n int c_global = group_start_channel + c_local;\n \n float val = input_ptr[i];\n float norm_val = (val - mean) * inv_std;\n \n // Apply affine transformation if weight/bias are provided\n if (weight != nullptr && bias != nullptr) {\n norm_val = weight[c_global] * norm_val + bias[c_global];\n }\n \n // Apply SiLU: x * sigmoid(x) = x / (1 + exp(-x))\n float silu_val = norm_val / (1.0f + expf(-norm_val));\n \n output_ptr[i] = silu_val;\n }\n}\n\nat::Tensor fused_groupnorm_silu_cuda(\n at::Tensor input,\n at::Tensor weight,\n at::Tensor bias,\n int num_groups,\n double eps\n) {\n int N = input.size(0);\n int C = input.size(1);\n int H = input.size(2);\n int W = input.size(3);\n int channels_per_group = C / num_groups;\n \n auto output = at::empty_like(input);\n \n int num_blocks = N * num_groups;\n int block_size = 256;\n \n const float* input_ptr = input.data_ptr<float>();\n float* output_ptr = output.data_ptr<float>();\n const float* weight_ptr = weight.defined() ? weight.data_ptr<float>() : nullptr;\n const float* bias_ptr = bias.defined() ? bias.data_ptr<float>() : nullptr;\n \n fused_groupnorm_silu_kernel<256><<<num_blocks, block_size>>>(\n input_ptr, output_ptr, weight_ptr, bias_ptr,\n N, C, H, W, num_groups, channels_per_group, static_cast<float>(eps)\n );\n \n return output;\n}\n\"\"\"\n\nCPP_SOURCE = r\"\"\"\nat::Tensor fused_groupnorm_silu_cuda(\n at::Tensor input,\n at::Tensor weight,\n at::Tensor bias,\n int num_groups,\n double eps\n);\n\"\"\"\n\n# Compile the extension\nfused_gn_silu = cpp_extension.load_inline(\n name='fused_gn_silu',\n cpp_sources=CPP_SOURCE,\n cuda_sources=CUDA_SOURCE,\n functions=['fused_groupnorm_silu_cuda'],\n extra_cuda_cflags=['-O3'],\n with_cuda=True,\n)\n\nimport torch\nimport torch.nn as nn\n\nclass ModelNew(nn.Module):\n \"\"\"\n Fused GroupNorm + SiLU using custom CUDA kernel.\n \"\"\"\n \n def __init__(\n self,\n num_channels: int,\n num_groups: int,\n eps: float = 1e-5,\n affine: bool = True,\n ):\n super().__init__()\n assert num_channels % num_groups == 0\n self.num_channels = num_channels\n self.num_groups = num_groups\n self.eps = eps\n self.affine = affine\n \n if affine:\n self.weight = nn.Parameter(torch.ones(num_channels))\n self.bias = nn.Parameter(torch.zeros(num_channels))\n else:\n self.register_parameter('weight', None)\n self.register_parameter('bias', None)\n \n def forward(self, x: torch.Tensor) -> torch.Tensor:\n \"\"\"\n Args:\n x: (N, C, H, W) feature map\n Returns:\n Same shape as x with GroupNorm + SiLU applied\n \"\"\"\n # Ensure contiguous input\n if not x.is_contiguous():\n x = x.contiguous()\n \n return fused_gn_silu.fused_groupnorm_silu_cuda(\n x,\n self.weight if self.weight is not None else torch.empty(0),\n self.bias if self.bias is not None else torch.empty(0),\n self.num_groups,\n self.eps\n )" }
result ← compile_kernel (ok)
okcompile_kernel compile_kernel PASSED: kernel compiled without errors.
turn 2 · 1 tool call(s) · 662.3s turn · 13.9s LLM · tokens in=3,595 · out=1,539 · total=5,134 · 7,318 chars (click to expand)
turn 2662.3s turn · 13.9s LLM · 648.4s tools · tokens in=3,595 · out=1,539 · total=5,134
reasoning
Good, it compiled. Now let me check correctness.
call → run_correctness
callrun_correctness { "kernel_code": "import torch\nimport torch.nn as nn\nimport torch.utils.cpp_extension as cpp_extension\n\nCUDA_SOURCE = r\"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <cmath>\n\n// Fused GroupNorm + SiLU kernel\n// Each thread block processes one (batch, group) pair\n// Threads within block cooperatively compute mean, variance, then apply normalization + SiLU\n\ntemplate <int BLOCK_SIZE>\n__global__ void fused_groupnorm_silu_kernel(\n const float* __restrict__ input,\n float* __restrict__ output,\n const float* __restrict__ weight,\n const float* __restrict__ bias,\n int N, int C, int H, int W, int G, int channels_per_group, float eps\n) {\n // Each block handles one (batch_item, group) pair\n int batch_idx = blockIdx.x / G;\n int group_idx = blockIdx.x % G;\n \n int group_start_channel = group_idx * channels_per_group;\n int group_size = channels_per_group * H * W;\n \n // Index for this thread's element within the group\n int tid = threadIdx.x;\n int total_elements = group_size;\n \n // Pointers to the start of this batch and group in input/output\n int batch_offset = batch_idx * C * H * W;\n int group_offset = group_start_channel * H * W;\n const float* __restrict__ input_ptr = input + batch_offset + group_offset;\n float* __restrict__ output_ptr = output + batch_offset + group_offset;\n \n // Shared memory for reduction\n __shared__ float s_mean[BLOCK_SIZE];\n __shared__ float s_var[BLOCK_SIZE];\n \n // Compute mean using parallel reduction\n float local_sum = 0.0f;\n float local_sq_sum = 0.0f;\n \n // Grid-stride loop to process all elements in the group\n for (int i = tid; i < total_elements; i += BLOCK_SIZE) {\n float val = input_ptr[i];\n local_sum += val;\n local_sq_sum += val * val;\n }\n \n s_mean[tid] = local_sum;\n s_var[tid] = local_sq_sum;\n __syncthreads();\n \n // Parallel reduction in shared memory\n for (int stride = BLOCK_SIZE / 2; stride > 0; stride >>= 1) {\n if (tid < stride) {\n s_mean[tid] += s_mean[tid + stride];\n s_var[tid] += s_var[tid + stride];\n }\n __syncthreads();\n }\n \n // Compute mean and variance\n float mean = 0.0f;\n float inv_std = 0.0f;\n if (tid == 0) {\n mean = s_mean[0] / total_elements;\n float variance = s_var[0] / total_elements - mean * mean;\n inv_std = 1.0f / sqrtf(variance + eps);\n s_mean[0] = mean;\n s_var[0] = inv_std;\n }\n __syncthreads();\n \n mean = s_mean[0];\n inv_std = s_var[0];\n \n // Apply normalization, affine, and SiLU\n for (int i = tid; i < total_elements; i += BLOCK_SIZE) {\n int c_local = i / (H * W);\n int c_global = group_start_channel + c_local;\n \n float val = input_ptr[i];\n float norm_val = (val - mean) * inv_std;\n \n // Apply affine transformation if weight/bias are provided\n if (weight != nullptr && bias != nullptr) {\n norm_val = weight[c_global] * norm_val + bias[c_global];\n }\n \n // Apply SiLU: x * sigmoid(x) = x / (1 + exp(-x))\n float silu_val = norm_val / (1.0f + expf(-norm_val));\n \n output_ptr[i] = silu_val;\n }\n}\n\nat::Tensor fused_groupnorm_silu_cuda(\n at::Tensor input,\n at::Tensor weight,\n at::Tensor bias,\n int num_groups,\n double eps\n) {\n int N = input.size(0);\n int C = input.size(1);\n int H = input.size(2);\n int W = input.size(3);\n int channels_per_group = C / num_groups;\n \n auto output = at::empty_like(input);\n \n int num_blocks = N * num_groups;\n int block_size = 256;\n \n const float* input_ptr = input.data_ptr<float>();\n float* output_ptr = output.data_ptr<float>();\n const float* weight_ptr = weight.defined() ? weight.data_ptr<float>() : nullptr;\n const float* bias_ptr = bias.defined() ? bias.data_ptr<float>() : nullptr;\n \n fused_groupnorm_silu_kernel<256><<<num_blocks, block_size>>>(\n input_ptr, output_ptr, weight_ptr, bias_ptr,\n N, C, H, W, num_groups, channels_per_group, static_cast<float>(eps)\n );\n \n return output;\n}\n\"\"\"\n\nCPP_SOURCE = r\"\"\"\nat::Tensor fused_groupnorm_silu_cuda(\n at::Tensor input,\n at::Tensor weight,\n at::Tensor bias,\n int num_groups,\n double eps\n);\n\"\"\"\n\n# Compile the extension\nfused_gn_silu = cpp_extension.load_inline(\n name='fused_gn_silu',\n cpp_sources=CPP_SOURCE,\n cuda_sources=CUDA_SOURCE,\n functions=['fused_groupnorm_silu_cuda'],\n extra_cuda_cflags=['-O3'],\n with_cuda=True,\n)\n\nimport torch\nimport torch.nn as nn\n\nclass ModelNew(nn.Module):\n \"\"\"\n Fused GroupNorm + SiLU using custom CUDA kernel.\n \"\"\"\n \n def __init__(\n self,\n num_channels: int,\n num_groups: int,\n eps: float = 1e-5,\n affine: bool = True,\n ):\n super().__init__()\n assert num_channels % num_groups == 0\n self.num_channels = num_channels\n self.num_groups = num_groups\n self.eps = eps\n self.affine = affine\n \n if affine:\n self.weight = nn.Parameter(torch.ones(num_channels))\n self.bias = nn.Parameter(torch.zeros(num_channels))\n else:\n self.register_parameter('weight', None)\n self.register_parameter('bias', None)\n \n def forward(self, x: torch.Tensor) -> torch.Tensor:\n \"\"\"\n Args:\n x: (N, C, H, W) feature map\n Returns:\n Same shape as x with GroupNorm + SiLU applied\n \"\"\"\n # Ensure contiguous input\n if not x.is_contiguous():\n x = x.contiguous()\n \n return fused_gn_silu.fused_groupnorm_silu_cuda(\n x,\n self.weight if self.weight is not None else torch.empty(0),\n self.bias if self.bias is not None else torch.empty(0),\n self.num_groups,\n self.eps\n )" }
result ← run_correctness (ok)
okrun_correctness run_correctness PASSED: (5 / 5) trials all matched the reference. Numerical precision: max_abs_err=1.43e-06 mean_abs_err=2.54e-08 max_rel_err=1.25e-02
turn 3 · 1 tool call(s) · 14.5s turn · 14.5s LLM · tokens in=5,180 · out=1,541 · total=6,721 · 7,234 chars (click to expand)
turn 314.5s turn · 14.5s LLM · 0.0s tools · tokens in=5,180 · out=1,541 · total=6,721
reasoning
The kernel passes correctness. Let me run a static check before submitting.
call → static_check
callstatic_check { "kernel_code": "import torch\nimport torch.nn as nn\nimport torch.utils.cpp_extension as cpp_extension\n\nCUDA_SOURCE = r\"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <cmath>\n\n// Fused GroupNorm + SiLU kernel\n// Each thread block processes one (batch, group) pair\n// Threads within block cooperatively compute mean, variance, then apply normalization + SiLU\n\ntemplate <int BLOCK_SIZE>\n__global__ void fused_groupnorm_silu_kernel(\n const float* __restrict__ input,\n float* __restrict__ output,\n const float* __restrict__ weight,\n const float* __restrict__ bias,\n int N, int C, int H, int W, int G, int channels_per_group, float eps\n) {\n // Each block handles one (batch_item, group) pair\n int batch_idx = blockIdx.x / G;\n int group_idx = blockIdx.x % G;\n \n int group_start_channel = group_idx * channels_per_group;\n int group_size = channels_per_group * H * W;\n \n // Index for this thread's element within the group\n int tid = threadIdx.x;\n int total_elements = group_size;\n \n // Pointers to the start of this batch and group in input/output\n int batch_offset = batch_idx * C * H * W;\n int group_offset = group_start_channel * H * W;\n const float* __restrict__ input_ptr = input + batch_offset + group_offset;\n float* __restrict__ output_ptr = output + batch_offset + group_offset;\n \n // Shared memory for reduction\n __shared__ float s_mean[BLOCK_SIZE];\n __shared__ float s_var[BLOCK_SIZE];\n \n // Compute mean using parallel reduction\n float local_sum = 0.0f;\n float local_sq_sum = 0.0f;\n \n // Grid-stride loop to process all elements in the group\n for (int i = tid; i < total_elements; i += BLOCK_SIZE) {\n float val = input_ptr[i];\n local_sum += val;\n local_sq_sum += val * val;\n }\n \n s_mean[tid] = local_sum;\n s_var[tid] = local_sq_sum;\n __syncthreads();\n \n // Parallel reduction in shared memory\n for (int stride = BLOCK_SIZE / 2; stride > 0; stride >>= 1) {\n if (tid < stride) {\n s_mean[tid] += s_mean[tid + stride];\n s_var[tid] += s_var[tid + stride];\n }\n __syncthreads();\n }\n \n // Compute mean and variance\n float mean = 0.0f;\n float inv_std = 0.0f;\n if (tid == 0) {\n mean = s_mean[0] / total_elements;\n float variance = s_var[0] / total_elements - mean * mean;\n inv_std = 1.0f / sqrtf(variance + eps);\n s_mean[0] = mean;\n s_var[0] = inv_std;\n }\n __syncthreads();\n \n mean = s_mean[0];\n inv_std = s_var[0];\n \n // Apply normalization, affine, and SiLU\n for (int i = tid; i < total_elements; i += BLOCK_SIZE) {\n int c_local = i / (H * W);\n int c_global = group_start_channel + c_local;\n \n float val = input_ptr[i];\n float norm_val = (val - mean) * inv_std;\n \n // Apply affine transformation if weight/bias are provided\n if (weight != nullptr && bias != nullptr) {\n norm_val = weight[c_global] * norm_val + bias[c_global];\n }\n \n // Apply SiLU: x * sigmoid(x) = x / (1 + exp(-x))\n float silu_val = norm_val / (1.0f + expf(-norm_val));\n \n output_ptr[i] = silu_val;\n }\n}\n\nat::Tensor fused_groupnorm_silu_cuda(\n at::Tensor input,\n at::Tensor weight,\n at::Tensor bias,\n int num_groups,\n double eps\n) {\n int N = input.size(0);\n int C = input.size(1);\n int H = input.size(2);\n int W = input.size(3);\n int channels_per_group = C / num_groups;\n \n auto output = at::empty_like(input);\n \n int num_blocks = N * num_groups;\n int block_size = 256;\n \n const float* input_ptr = input.data_ptr<float>();\n float* output_ptr = output.data_ptr<float>();\n const float* weight_ptr = weight.defined() ? weight.data_ptr<float>() : nullptr;\n const float* bias_ptr = bias.defined() ? bias.data_ptr<float>() : nullptr;\n \n fused_groupnorm_silu_kernel<256><<<num_blocks, block_size>>>(\n input_ptr, output_ptr, weight_ptr, bias_ptr,\n N, C, H, W, num_groups, channels_per_group, static_cast<float>(eps)\n );\n \n return output;\n}\n\"\"\"\n\nCPP_SOURCE = r\"\"\"\nat::Tensor fused_groupnorm_silu_cuda(\n at::Tensor input,\n at::Tensor weight,\n at::Tensor bias,\n int num_groups,\n double eps\n);\n\"\"\"\n\n# Compile the extension\nfused_gn_silu = cpp_extension.load_inline(\n name='fused_gn_silu',\n cpp_sources=CPP_SOURCE,\n cuda_sources=CUDA_SOURCE,\n functions=['fused_groupnorm_silu_cuda'],\n extra_cuda_cflags=['-O3'],\n with_cuda=True,\n)\n\nimport torch\nimport torch.nn as nn\n\nclass ModelNew(nn.Module):\n \"\"\"\n Fused GroupNorm + SiLU using custom CUDA kernel.\n \"\"\"\n \n def __init__(\n self,\n num_channels: int,\n num_groups: int,\n eps: float = 1e-5,\n affine: bool = True,\n ):\n super().__init__()\n assert num_channels % num_groups == 0\n self.num_channels = num_channels\n self.num_groups = num_groups\n self.eps = eps\n self.affine = affine\n \n if affine:\n self.weight = nn.Parameter(torch.ones(num_channels))\n self.bias = nn.Parameter(torch.zeros(num_channels))\n else:\n self.register_parameter('weight', None)\n self.register_parameter('bias', None)\n \n def forward(self, x: torch.Tensor) -> torch.Tensor:\n \"\"\"\n Args:\n x: (N, C, H, W) feature map\n Returns:\n Same shape as x with GroupNorm + SiLU applied\n \"\"\"\n # Ensure contiguous input\n if not x.is_contiguous():\n x = x.contiguous()\n \n return fused_gn_silu.fused_groupnorm_silu_cuda(\n x,\n self.weight if self.weight is not None else torch.empty(0),\n self.bias if self.bias is not None else torch.empty(0),\n self.num_groups,\n self.eps\n )" }
result ← static_check (ok)
okstatic_check static_check PASSED: no violations or warnings detected.
turn 4 · FINAL · 1 tool call(s) · 202.6s turn · 15.1s LLM · tokens in=6,721 · out=1,556 · total=8,277 · 7,683 chars (click to expand)
turn 4202.6s turn · 15.1s LLM · 187.5s tools · tokens in=6,721 · out=1,556 · total=8,277 · FINAL
reasoning
The kernel passes both correctness and static checks. Let me submit it now.
assistantThe kernel passes all checks. Let me submit it for final evaluation.
call → submit_kernel
callsubmit_kernel { "kernel_code": "import torch\nimport torch.nn as nn\nimport torch.utils.cpp_extension as cpp_extension\n\nCUDA_SOURCE = r\"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <cmath>\n\n// Fused GroupNorm + SiLU kernel\n// Each thread block processes one (batch, group) pair\n// Threads within block cooperatively compute mean, variance, then apply normalization + SiLU\n\ntemplate <int BLOCK_SIZE>\n__global__ void fused_groupnorm_silu_kernel(\n const float* __restrict__ input,\n float* __restrict__ output,\n const float* __restrict__ weight,\n const float* __restrict__ bias,\n int N, int C, int H, int W, int G, int channels_per_group, float eps\n) {\n // Each block handles one (batch_item, group) pair\n int batch_idx = blockIdx.x / G;\n int group_idx = blockIdx.x % G;\n \n int group_start_channel = group_idx * channels_per_group;\n int group_size = channels_per_group * H * W;\n \n // Index for this thread's element within the group\n int tid = threadIdx.x;\n int total_elements = group_size;\n \n // Pointers to the start of this batch and group in input/output\n int batch_offset = batch_idx * C * H * W;\n int group_offset = group_start_channel * H * W;\n const float* __restrict__ input_ptr = input + batch_offset + group_offset;\n float* __restrict__ output_ptr = output + batch_offset + group_offset;\n \n // Shared memory for reduction\n __shared__ float s_mean[BLOCK_SIZE];\n __shared__ float s_var[BLOCK_SIZE];\n \n // Compute mean using parallel reduction\n float local_sum = 0.0f;\n float local_sq_sum = 0.0f;\n \n // Grid-stride loop to process all elements in the group\n for (int i = tid; i < total_elements; i += BLOCK_SIZE) {\n float val = input_ptr[i];\n local_sum += val;\n local_sq_sum += val * val;\n }\n \n s_mean[tid] = local_sum;\n s_var[tid] = local_sq_sum;\n __syncthreads();\n \n // Parallel reduction in shared memory\n for (int stride = BLOCK_SIZE / 2; stride > 0; stride >>= 1) {\n if (tid < stride) {\n s_mean[tid] += s_mean[tid + stride];\n s_var[tid] += s_var[tid + stride];\n }\n __syncthreads();\n }\n \n // Compute mean and variance\n float mean = 0.0f;\n float inv_std = 0.0f;\n if (tid == 0) {\n mean = s_mean[0] / total_elements;\n float variance = s_var[0] / total_elements - mean * mean;\n inv_std = 1.0f / sqrtf(variance + eps);\n s_mean[0] = mean;\n s_var[0] = inv_std;\n }\n __syncthreads();\n \n mean = s_mean[0];\n inv_std = s_var[0];\n \n // Apply normalization, affine, and SiLU\n for (int i = tid; i < total_elements; i += BLOCK_SIZE) {\n int c_local = i / (H * W);\n int c_global = group_start_channel + c_local;\n \n float val = input_ptr[i];\n float norm_val = (val - mean) * inv_std;\n \n // Apply affine transformation if weight/bias are provided\n if (weight != nullptr && bias != nullptr) {\n norm_val = weight[c_global] * norm_val + bias[c_global];\n }\n \n // Apply SiLU: x * sigmoid(x) = x / (1 + exp(-x))\n float silu_val = norm_val / (1.0f + expf(-norm_val));\n \n output_ptr[i] = silu_val;\n }\n}\n\nat::Tensor fused_groupnorm_silu_cuda(\n at::Tensor input,\n at::Tensor weight,\n at::Tensor bias,\n int num_groups,\n double eps\n) {\n int N = input.size(0);\n int C = input.size(1);\n int H = input.size(2);\n int W = input.size(3);\n int channels_per_group = C / num_groups;\n \n auto output = at::empty_like(input);\n \n int num_blocks = N * num_groups;\n int block_size = 256;\n \n const float* input_ptr = input.data_ptr<float>();\n float* output_ptr = output.data_ptr<float>();\n const float* weight_ptr = weight.defined() ? weight.data_ptr<float>() : nullptr;\n const float* bias_ptr = bias.defined() ? bias.data_ptr<float>() : nullptr;\n \n fused_groupnorm_silu_kernel<256><<<num_blocks, block_size>>>(\n input_ptr, output_ptr, weight_ptr, bias_ptr,\n N, C, H, W, num_groups, channels_per_group, static_cast<float>(eps)\n );\n \n return output;\n}\n\"\"\"\n\nCPP_SOURCE = r\"\"\"\nat::Tensor fused_groupnorm_silu_cuda(\n at::Tensor input,\n at::Tensor weight,\n at::Tensor bias,\n int num_groups,\n double eps\n);\n\"\"\"\n\n# Compile the extension\nfused_gn_silu = cpp_extension.load_inline(\n name='fused_gn_silu',\n cpp_sources=CPP_SOURCE,\n cuda_sources=CUDA_SOURCE,\n functions=['fused_groupnorm_silu_cuda'],\n extra_cuda_cflags=['-O3'],\n with_cuda=True,\n)\n\nimport torch\nimport torch.nn as nn\n\nclass ModelNew(nn.Module):\n \"\"\"\n Fused GroupNorm + SiLU using custom CUDA kernel.\n \"\"\"\n \n def __init__(\n self,\n num_channels: int,\n num_groups: int,\n eps: float = 1e-5,\n affine: bool = True,\n ):\n super().__init__()\n assert num_channels % num_groups == 0\n self.num_channels = num_channels\n self.num_groups = num_groups\n self.eps = eps\n self.affine = affine\n \n if affine:\n self.weight = nn.Parameter(torch.ones(num_channels))\n self.bias = nn.Parameter(torch.zeros(num_channels))\n else:\n self.register_parameter('weight', None)\n self.register_parameter('bias', None)\n \n def forward(self, x: torch.Tensor) -> torch.Tensor:\n \"\"\"\n Args:\n x: (N, C, H, W) feature map\n Returns:\n Same shape as x with GroupNorm + SiLU applied\n \"\"\"\n # Ensure contiguous input\n if not x.is_contiguous():\n x = x.contiguous()\n \n return fused_gn_silu.fused_groupnorm_silu_cuda(\n x,\n self.weight if self.weight is not None else torch.empty(0),\n self.bias if self.bias is not None else torch.empty(0),\n self.num_groups,\n self.eps\n )" }
result ← submit_kernel (ok)
oksubmit_kernel submit_kernel PASSED: (5 / 5) correctness trials all passed. Kernel runtime: 0.01 μs Runtime stats: mean=0.01μs median=?μs std=0.00μs Numerical precision: max_abs_err=1.43e-06 mean_abs_err=2.54e-08 max_rel_err=1.25e-02 Memory: 42.0 MB peak (ref: 46.5 MB, ratio: 0.9032x) Kernel launches: 2 (ref: 12, fusion ratio: 6.0) SOL score: 0.2198
final submitted kernel (201 lines, 5,951 chars) — full source, untruncated
import torch
import torch.nn as nn
import torch.utils.cpp_extension as cpp_extension

CUDA_SOURCE = r"""
#include <torch/extension.h>
#include <cuda.h>
#include <cuda_runtime.h>
#include <cmath>

// Fused GroupNorm + SiLU kernel
// Each thread block processes one (batch, group) pair
// Threads within block cooperatively compute mean, variance, then apply normalization + SiLU

template <int BLOCK_SIZE>
__global__ void fused_groupnorm_silu_kernel(
    const float* __restrict__ input,
    float* __restrict__ output,
    const float* __restrict__ weight,
    const float* __restrict__ bias,
    int N, int C, int H, int W, int G, int channels_per_group, float eps
) {
    // Each block handles one (batch_item, group) pair
    int batch_idx = blockIdx.x / G;
    int group_idx = blockIdx.x % G;
    
    int group_start_channel = group_idx * channels_per_group;
    int group_size = channels_per_group * H * W;
    
    // Index for this thread's element within the group
    int tid = threadIdx.x;
    int total_elements = group_size;
    
    // Pointers to the start of this batch and group in input/output
    int batch_offset = batch_idx * C * H * W;
    int group_offset = group_start_channel * H * W;
    const float* __restrict__ input_ptr = input + batch_offset + group_offset;
    float* __restrict__ output_ptr = output + batch_offset + group_offset;
    
    // Shared memory for reduction
    __shared__ float s_mean[BLOCK_SIZE];
    __shared__ float s_var[BLOCK_SIZE];
    
    // Compute mean using parallel reduction
    float local_sum = 0.0f;
    float local_sq_sum = 0.0f;
    
    // Grid-stride loop to process all elements in the group
    for (int i = tid; i < total_elements; i += BLOCK_SIZE) {
        float val = input_ptr[i];
        local_sum += val;
        local_sq_sum += val * val;
    }
    
    s_mean[tid] = local_sum;
    s_var[tid] = local_sq_sum;
    __syncthreads();
    
    // Parallel reduction in shared memory
    for (int stride = BLOCK_SIZE / 2; stride > 0; stride >>= 1) {
        if (tid < stride) {
            s_mean[tid] += s_mean[tid + stride];
            s_var[tid] += s_var[tid + stride];
        }
        __syncthreads();
    }
    
    // Compute mean and variance
    float mean = 0.0f;
    float inv_std = 0.0f;
    if (tid == 0) {
        mean = s_mean[0] / total_elements;
        float variance = s_var[0] / total_elements - mean * mean;
        inv_std = 1.0f / sqrtf(variance + eps);
        s_mean[0] = mean;
        s_var[0] = inv_std;
    }
    __syncthreads();
    
    mean = s_mean[0];
    inv_std = s_var[0];
    
    // Apply normalization, affine, and SiLU
    for (int i = tid; i < total_elements; i += BLOCK_SIZE) {
        int c_local = i / (H * W);
        int c_global = group_start_channel + c_local;
        
        float val = input_ptr[i];
        float norm_val = (val - mean) * inv_std;
        
        // Apply affine transformation if weight/bias are provided
        if (weight != nullptr && bias != nullptr) {
            norm_val = weight[c_global] * norm_val + bias[c_global];
        }
        
        // Apply SiLU: x * sigmoid(x) = x / (1 + exp(-x))
        float silu_val = norm_val / (1.0f + expf(-norm_val));
        
        output_ptr[i] = silu_val;
    }
}

at::Tensor fused_groupnorm_silu_cuda(
    at::Tensor input,
    at::Tensor weight,
    at::Tensor bias,
    int num_groups,
    double eps
) {
    int N = input.size(0);
    int C = input.size(1);
    int H = input.size(2);
    int W = input.size(3);
    int channels_per_group = C / num_groups;
    
    auto output = at::empty_like(input);
    
    int num_blocks = N * num_groups;
    int block_size = 256;
    
    const float* input_ptr = input.data_ptr<float>();
    float* output_ptr = output.data_ptr<float>();
    const float* weight_ptr = weight.defined() ? weight.data_ptr<float>() : nullptr;
    const float* bias_ptr = bias.defined() ? bias.data_ptr<float>() : nullptr;
    
    fused_groupnorm_silu_kernel<256><<<num_blocks, block_size>>>(
        input_ptr, output_ptr, weight_ptr, bias_ptr,
        N, C, H, W, num_groups, channels_per_group, static_cast<float>(eps)
    );
    
    return output;
}
"""

CPP_SOURCE = r"""
at::Tensor fused_groupnorm_silu_cuda(
    at::Tensor input,
    at::Tensor weight,
    at::Tensor bias,
    int num_groups,
    double eps
);
"""

# Compile the extension
fused_gn_silu = cpp_extension.load_inline(
    name='fused_gn_silu',
    cpp_sources=CPP_SOURCE,
    cuda_sources=CUDA_SOURCE,
    functions=['fused_groupnorm_silu_cuda'],
    extra_cuda_cflags=['-O3'],
    with_cuda=True,
)

import torch
import torch.nn as nn

class ModelNew(nn.Module):
    """
    Fused GroupNorm + SiLU using custom CUDA kernel.
    """
    
    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.num_channels = num_channels
        self.num_groups = num_groups
        self.eps = eps
        self.affine = affine
        
        if affine:
            self.weight = nn.Parameter(torch.ones(num_channels))
            self.bias = nn.Parameter(torch.zeros(num_channels))
        else:
            self.register_parameter('weight', None)
            self.register_parameter('bias', None)
    
    def forward(self, x: torch.Tensor) -> torch.Tensor:
        """
        Args:
            x: (N, C, H, W) feature map
        Returns:
            Same shape as x with GroupNorm + SiLU applied
        """
        # Ensure contiguous input
        if not x.is_contiguous():
            x = x.contiguous()
        
        return fused_gn_silu.fused_groupnorm_silu_cuda(
            x,
            self.weight if self.weight is not None else torch.empty(0),
            self.bias if self.bias is not None else torch.empty(0),
            self.num_groups,
            self.eps
        )