correct
34_InstanceNorm.py
started
2026-05-05T00:34:49Z
timingkernelbench.timing
kernel mean / median / std
7.93 / — / 0.44 μs
ref mean / median / std
9.84 / — / 2.06 μs
numerical precisioneval_kernel_against_ref
gpu memoryextended_metrics.measure_memory
kernel launches / fusiontorch.profiler
fusion ratio (ref / kernel)
8.00x
energyNVML (pynvml)
ratio (ref / kernel)
1.33x
SOL (speed-of-light)nsight_hardware_counters
roofline / occupancynsight_hardware_counters
dram bandwidth
2917.87 GB/s
warp stalls (top 5)nsight
reference PyTorch source (the prompt the agent sees)
import torch
import torch.nn as nn
class Model(nn.Module):
"""
Simple model that performs Instance Normalization.
"""
def __init__(self, num_features: int):
"""
Initializes the InstanceNorm layer.
Args:
num_features (int): Number of features in the input tensor.
"""
super(Model, self).__init__()
self.inorm = nn.InstanceNorm2d(num_features=num_features)
def forward(self, x: torch.Tensor) -> torch.Tensor:
"""
Applies Instance Normalization to the input tensor.
Args:
x (torch.Tensor): Input tensor of shape (batch_size, num_features, height, width).
Returns:
torch.Tensor: Output tensor with Instance Normalization applied, same shape as input.
"""
return self.inorm(x)
batch_size = 112 # heavier workload
features = 64
dim1 = 512
dim2 = 512
def get_inputs():
x = torch.rand(batch_size, features, dim1, dim2)
return [x]
def get_init_inputs():
return [features]turn 0 · FINAL · 1 tool call(s) · 63.0s turn · 63.0s LLM · 7,745 chars (click to expand)
turn 063.0s turn · 63.0s LLM · 0.0s tools · FINAL
reasoning
**Evaluating performance metrics**
I'm looking into the input-output performance of our project. It seems like referencing PyTorch might be relevant since it probably uses cuDNN. I'm considering whether a custom single kernel might actually outperform it. However, I'm realizing that PyTorch’s instance normalization may involve multiple kernels, which could be slower. I'm keeping track of our single kernel memory usage at around 22.5GB, which processes at approximately 15ms on high-end systems.
**Submitting training model settings**
I’m thinking about the default training model reference. It seems that with `track_running_stats` set to false, it will behave the same in evaluation mode, which is a good thing. I’m checking to ensure there are no parameters to adjust. It sounds like everything is in order, so I’m ready to submit this configuration. Now it’s time to proceed!
call → submit_kernel
callsubmit_kernel
{
"kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\n_cpp_src = r'''\n#include <torch/extension.h>\n\ntorch::Tensor instance_norm_forward_cuda(torch::Tensor x, double eps);\n\ntorch::Tensor instance_norm_forward(torch::Tensor x, double eps) {\n return instance_norm_forward_cuda(x, eps);\n}\n'''\n\n_cuda_src = r'''\n#include <torch/extension.h>\n#include <ATen/cuda/CUDAContext.h>\n#include <c10/cuda/CUDAException.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <stdint.h>\n\n__inline__ __device__ float warp_reduce_sum(float v) {\n v += __shfl_down_sync(0xffffffff, v, 16);\n v += __shfl_down_sync(0xffffffff, v, 8);\n v += __shfl_down_sync(0xffffffff, v, 4);\n v += __shfl_down_sync(0xffffffff, v, 2);\n v += __shfl_down_sync(0xffffffff, v, 1);\n return v;\n}\n\ntemplate<int BLOCK>\n__global__ void instancenorm2d_fwd_kernel(const float* __restrict__ x,\n float* __restrict__ y,\n int64_t rows,\n int64_t hw,\n float eps) {\n const int row = (int)blockIdx.x;\n if ((int64_t)row >= rows) return;\n\n const int tid = threadIdx.x;\n const int lane = tid & 31;\n const int warp = tid >> 5;\n\n const float* __restrict__ xp = x + (int64_t)row * hw;\n float* __restrict__ yp = y + (int64_t)row * hw;\n\n float sum = 0.0f;\n float sumsq = 0.0f;\n\n // Fast vector path for the benchmark shape (H*W divisible by 4 and aligned).\n if (((hw & 3LL) == 0) && ((((uintptr_t)xp) & 15ULL) == 0) && ((((uintptr_t)yp) & 15ULL) == 0)) {\n const float4* __restrict__ x4 = reinterpret_cast<const float4*>(xp);\n const int64_t n4 = hw >> 2;\n for (int64_t i = tid; i < n4; i += BLOCK) {\n float4 v = x4[i];\n sum += (v.x + v.y) + (v.z + v.w);\n sumsq = fmaf(v.x, v.x, sumsq);\n sumsq = fmaf(v.y, v.y, sumsq);\n sumsq = fmaf(v.z, v.z, sumsq);\n sumsq = fmaf(v.w, v.w, sumsq);\n }\n } else {\n for (int64_t i = tid; i < hw; i += BLOCK) {\n float v = xp[i];\n sum += v;\n sumsq = fmaf(v, v, sumsq);\n }\n }\n\n __shared__ float sh_sum[32];\n __shared__ float sh_sumsq[32];\n __shared__ float sh_param[2];\n\n sum = warp_reduce_sum(sum);\n sumsq = warp_reduce_sum(sumsq);\n if (lane == 0) {\n sh_sum[warp] = sum;\n sh_sumsq[warp] = sumsq;\n }\n __syncthreads();\n\n if (warp == 0) {\n float bsum = (lane < (BLOCK >> 5)) ? sh_sum[lane] : 0.0f;\n float bsumsq = (lane < (BLOCK >> 5)) ? sh_sumsq[lane] : 0.0f;\n bsum = warp_reduce_sum(bsum);\n bsumsq = warp_reduce_sum(bsumsq);\n if (lane == 0) {\n float inv_hw = 1.0f / (float)hw;\n float mean = bsum * inv_hw;\n float var = bsumsq * inv_hw - mean * mean;\n var = var < 0.0f ? 0.0f : var;\n sh_param[0] = mean;\n sh_param[1] = rsqrtf(var + eps);\n }\n }\n __syncthreads();\n\n const float mean = sh_param[0];\n const float invstd = sh_param[1];\n\n if (((hw & 3LL) == 0) && ((((uintptr_t)xp) & 15ULL) == 0) && ((((uintptr_t)yp) & 15ULL) == 0)) {\n const float4* __restrict__ x4 = reinterpret_cast<const float4*>(xp);\n float4* __restrict__ y4 = reinterpret_cast<float4*>(yp);\n const int64_t n4 = hw >> 2;\n for (int64_t i = tid; i < n4; i += BLOCK) {\n float4 v = x4[i];\n v.x = (v.x - mean) * invstd;\n v.y = (v.y - mean) * invstd;\n v.z = (v.z - mean) * invstd;\n v.w = (v.w - mean) * invstd;\n y4[i] = v;\n }\n } else {\n for (int64_t i = tid; i < hw; i += BLOCK) {\n yp[i] = (xp[i] - mean) * invstd;\n }\n }\n}\n\ntorch::Tensor instance_norm_forward_cuda(torch::Tensor x, double eps) {\n TORCH_CHECK(x.is_cuda(), \"input must be a CUDA tensor\");\n TORCH_CHECK(x.scalar_type() == torch::kFloat32, \"only float32 is supported\");\n TORCH_CHECK(x.dim() == 4, \"input must be NCHW\");\n\n if (!x.is_contiguous()) {\n x = x.contiguous();\n }\n\n auto y = torch::empty_like(x);\n const int64_t n = x.size(0);\n const int64_t c = x.size(1);\n const int64_t h = x.size(2);\n const int64_t w = x.size(3);\n const int64_t rows = n * c;\n const int64_t hw = h * w;\n\n if (rows == 0 || hw == 0) return y;\n\n constexpr int BLOCK = 256;\n dim3 block(BLOCK);\n dim3 grid((unsigned int)rows);\n cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n instancenorm2d_fwd_kernel<BLOCK><<<grid, block, 0, stream>>>(\n x.data_ptr<float>(), y.data_ptr<float>(), rows, hw, (float)eps);\n C10_CUDA_KERNEL_LAUNCH_CHECK();\n return y;\n}\n'''\n\n_ext = load_inline(\n name='instancenorm2d_fwd_inline_opt',\n cpp_sources=[_cpp_src],\n cuda_sources=[_cuda_src],\n functions=['instance_norm_forward'],\n extra_cflags=['-O3'],\n extra_cuda_cflags=['-O3', '--use_fast_math'],\n with_cuda=True,\n verbose=False,\n)\n\nclass ModelNew(nn.Module):\n def __init__(self, num_features: int):\n super(ModelNew, self).__init__()\n self.num_features = num_features\n\n def forward(self, x: torch.Tensor) -> torch.Tensor:\n return _ext.instance_norm_forward(x, 1.0e-5)\n"
}
result ← submit_kernel (ok)
oksubmit_kernel
submit_kernel PASSED: (5 / 5) correctness trials all passed.
Kernel runtime: 7.93 μs
Runtime stats: mean=7.93μs median=?μs std=0.44μs
Numerical precision: max_abs_err=1.67e-06 mean_abs_err=1.44e-07 max_rel_err=4.13e+05
Memory: 14369.0 MB peak (ref: 14369.05 MB, ratio: 1.0x)
Kernel launches: 2 (ref: 16, fusion ratio: 8.0)
Energy: 1450.88 mJ/run (ref: 1934.04 mJ, ratio: 1.333x)
SOL score: 0.871
final submitted kernel (169 lines, 5,327 chars) — full source, untruncated
import torch
import torch.nn as nn
from torch.utils.cpp_extension import load_inline
_cpp_src = r'''
#include <torch/extension.h>
torch::Tensor instance_norm_forward_cuda(torch::Tensor x, double eps);
torch::Tensor instance_norm_forward(torch::Tensor x, double eps) {
return instance_norm_forward_cuda(x, eps);
}
'''
_cuda_src = r'''
#include <torch/extension.h>
#include <ATen/cuda/CUDAContext.h>
#include <c10/cuda/CUDAException.h>
#include <cuda.h>
#include <cuda_runtime.h>
#include <stdint.h>
__inline__ __device__ float warp_reduce_sum(float v) {
v += __shfl_down_sync(0xffffffff, v, 16);
v += __shfl_down_sync(0xffffffff, v, 8);
v += __shfl_down_sync(0xffffffff, v, 4);
v += __shfl_down_sync(0xffffffff, v, 2);
v += __shfl_down_sync(0xffffffff, v, 1);
return v;
}
template<int BLOCK>
__global__ void instancenorm2d_fwd_kernel(const float* __restrict__ x,
float* __restrict__ y,
int64_t rows,
int64_t hw,
float eps) {
const int row = (int)blockIdx.x;
if ((int64_t)row >= rows) return;
const int tid = threadIdx.x;
const int lane = tid & 31;
const int warp = tid >> 5;
const float* __restrict__ xp = x + (int64_t)row * hw;
float* __restrict__ yp = y + (int64_t)row * hw;
float sum = 0.0f;
float sumsq = 0.0f;
// Fast vector path for the benchmark shape (H*W divisible by 4 and aligned).
if (((hw & 3LL) == 0) && ((((uintptr_t)xp) & 15ULL) == 0) && ((((uintptr_t)yp) & 15ULL) == 0)) {
const float4* __restrict__ x4 = reinterpret_cast<const float4*>(xp);
const int64_t n4 = hw >> 2;
for (int64_t i = tid; i < n4; i += BLOCK) {
float4 v = x4[i];
sum += (v.x + v.y) + (v.z + v.w);
sumsq = fmaf(v.x, v.x, sumsq);
sumsq = fmaf(v.y, v.y, sumsq);
sumsq = fmaf(v.z, v.z, sumsq);
sumsq = fmaf(v.w, v.w, sumsq);
}
} else {
for (int64_t i = tid; i < hw; i += BLOCK) {
float v = xp[i];
sum += v;
sumsq = fmaf(v, v, sumsq);
}
}
__shared__ float sh_sum[32];
__shared__ float sh_sumsq[32];
__shared__ float sh_param[2];
sum = warp_reduce_sum(sum);
sumsq = warp_reduce_sum(sumsq);
if (lane == 0) {
sh_sum[warp] = sum;
sh_sumsq[warp] = sumsq;
}
__syncthreads();
if (warp == 0) {
float bsum = (lane < (BLOCK >> 5)) ? sh_sum[lane] : 0.0f;
float bsumsq = (lane < (BLOCK >> 5)) ? sh_sumsq[lane] : 0.0f;
bsum = warp_reduce_sum(bsum);
bsumsq = warp_reduce_sum(bsumsq);
if (lane == 0) {
float inv_hw = 1.0f / (float)hw;
float mean = bsum * inv_hw;
float var = bsumsq * inv_hw - mean * mean;
var = var < 0.0f ? 0.0f : var;
sh_param[0] = mean;
sh_param[1] = rsqrtf(var + eps);
}
}
__syncthreads();
const float mean = sh_param[0];
const float invstd = sh_param[1];
if (((hw & 3LL) == 0) && ((((uintptr_t)xp) & 15ULL) == 0) && ((((uintptr_t)yp) & 15ULL) == 0)) {
const float4* __restrict__ x4 = reinterpret_cast<const float4*>(xp);
float4* __restrict__ y4 = reinterpret_cast<float4*>(yp);
const int64_t n4 = hw >> 2;
for (int64_t i = tid; i < n4; i += BLOCK) {
float4 v = x4[i];
v.x = (v.x - mean) * invstd;
v.y = (v.y - mean) * invstd;
v.z = (v.z - mean) * invstd;
v.w = (v.w - mean) * invstd;
y4[i] = v;
}
} else {
for (int64_t i = tid; i < hw; i += BLOCK) {
yp[i] = (xp[i] - mean) * invstd;
}
}
}
torch::Tensor instance_norm_forward_cuda(torch::Tensor x, double eps) {
TORCH_CHECK(x.is_cuda(), "input must be a CUDA tensor");
TORCH_CHECK(x.scalar_type() == torch::kFloat32, "only float32 is supported");
TORCH_CHECK(x.dim() == 4, "input must be NCHW");
if (!x.is_contiguous()) {
x = x.contiguous();
}
auto y = torch::empty_like(x);
const int64_t n = x.size(0);
const int64_t c = x.size(1);
const int64_t h = x.size(2);
const int64_t w = x.size(3);
const int64_t rows = n * c;
const int64_t hw = h * w;
if (rows == 0 || hw == 0) return y;
constexpr int BLOCK = 256;
dim3 block(BLOCK);
dim3 grid((unsigned int)rows);
cudaStream_t stream = at::cuda::getCurrentCUDAStream();
instancenorm2d_fwd_kernel<BLOCK><<<grid, block, 0, stream>>>(
x.data_ptr<float>(), y.data_ptr<float>(), rows, hw, (float)eps);
C10_CUDA_KERNEL_LAUNCH_CHECK();
return y;
}
'''
_ext = load_inline(
name='instancenorm2d_fwd_inline_opt',
cpp_sources=[_cpp_src],
cuda_sources=[_cuda_src],
functions=['instance_norm_forward'],
extra_cflags=['-O3'],
extra_cuda_cflags=['-O3', '--use_fast_math'],
with_cuda=True,
verbose=False,
)
class ModelNew(nn.Module):
def __init__(self, num_features: int):
super(ModelNew, self).__init__()
self.num_features = num_features
def forward(self, x: torch.Tensor) -> torch.Tensor:
return _ext.instance_norm_forward(x, 1.0e-5)