correct
94_MSELoss.py
started
2026-05-05T00:02:02Z
timingkernelbench.timing
kernel mean / median / std
18.10 / — / 72.50 μs
ref mean / median / std
8.50 / — / 0.08 μs
numerical precisioneval_kernel_against_ref
gpu memoryextended_metrics.measure_memory
kernel launches / fusiontorch.profiler
fusion ratio (ref / kernel)
3.50x
energyNVML (pynvml)
ratio (ref / kernel)
0.33x
SOL (speed-of-light)nsight_hardware_counters
roofline / occupancynsight_hardware_counters
dram bandwidth
2915.25 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):
"""
A model that computes the Mean Squared Error loss for regression tasks.
Parameters:
None
"""
def __init__(self):
super(Model, self).__init__()
def forward(self, predictions, targets):
return torch.mean((predictions - targets) ** 2)
batch_size = 32768
input_shape = (32768,)
dim = 1
def get_inputs():
scale = torch.rand(())
return [torch.rand(batch_size, *input_shape)*scale, torch.rand(batch_size, *input_shape)]
def get_init_inputs():
return []
turn 0 · FINAL · 1 tool call(s) · 148.6s turn · 148.6s LLM · 7,935 chars (click to expand)
turn 0148.6s turn · 148.6s LLM · 0.0s tools · FINAL
reasoning
**Optimizing Python for MSE Calculation**
I need to submit a complete Python implementation to optimize the mean squared error (MSE) over a huge tensor with a shape of batch_size=32768 and input_shape=(32768). This results in a staggering number of elements! Using PyTorch, I'm faced with potential memory issues while calculating squared differences. I'll need to implement an efficient reduction, ensure contiguity, and maintain numerical equivalence. My goal is to achieve a fast calculation while respecting accuracy tolerances.
**Optimizing Reduction in CUDA**
I’m considering the strategy for accumulating per-thread floats, thinking grid stride might help, but I also wonder if Kahan summation is necessary. The error can get high, so I need to be cautious. Memory usage is a real concern, and I want to avoid excessive storage of partial sums. Using a two-phase reduction with kernel1 and kernel2 could help manage this. I'll ensure the process remains efficient with CUDA, and check if I need to handle CPU tasks as well.
**Using C++ Functions with CUDA**
I don't need to use PYBIND for this. I can define C++ functions directly in the cuda_sources. When I use load_inline, it should automatically bind the functions found. Typically, I need to include cpp_sources for declarations, and I see the example is clear: load_inline with the appropriate parameters. I should ensure that the functions are available in cpp, as this seems important for generating the necessary binding code that references function prototypes. This process might prepend includes too.
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 mse_forward(torch::Tensor predictions, torch::Tensor targets);\n\"\"\"\n\n_cuda_src = r\"\"\"\n#include <torch/extension.h>\n#include <ATen/cuda/CUDAContext.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n\n// Warp and block reductions for float sums.\n__inline__ __device__ float warp_reduce_sum(float v) {\n #pragma unroll\n for (int offset = 16; offset > 0; offset >>= 1) {\n v += __shfl_down_sync(0xffffffff, v, offset);\n }\n return v;\n}\n\n__inline__ __device__ float block_reduce_sum(float v) {\n static __shared__ float shared[32];\n int lane = threadIdx.x & 31;\n int wid = threadIdx.x >> 5;\n v = warp_reduce_sum(v);\n if (lane == 0) shared[wid] = v;\n __syncthreads();\n v = (threadIdx.x < (blockDim.x >> 5)) ? shared[lane] : 0.0f;\n if (wid == 0) v = warp_reduce_sum(v);\n return v;\n}\n\ntemplate<int BLOCK_SIZE>\n__global__ __launch_bounds__(BLOCK_SIZE, 2)\nvoid mse_partial_kernel(const float* __restrict__ pred,\n const float* __restrict__ targ,\n float* __restrict__ partial,\n int64_t n) {\n float sum = 0.0f;\n int64_t tid = (int64_t)blockIdx.x * BLOCK_SIZE + threadIdx.x;\n int64_t stride = (int64_t)gridDim.x * BLOCK_SIZE;\n\n // Grid-stride loop with four independent accumulators improves ILP and reduces\n // rounding error a little versus one long dependency chain.\n float s0 = 0.0f, s1 = 0.0f, s2 = 0.0f, s3 = 0.0f;\n int64_t i = tid;\n int64_t step4 = stride * 4;\n for (; i + 3 * stride < n; i += step4) {\n float d0 = pred[i] - targ[i];\n float d1 = pred[i + stride] - targ[i + stride];\n float d2 = pred[i + 2 * stride] - targ[i + 2 * stride];\n float d3 = pred[i + 3 * stride] - targ[i + 3 * stride];\n s0 = fmaf(d0, d0, s0);\n s1 = fmaf(d1, d1, s1);\n s2 = fmaf(d2, d2, s2);\n s3 = fmaf(d3, d3, s3);\n }\n sum = (s0 + s1) + (s2 + s3);\n for (; i < n; i += stride) {\n float d = pred[i] - targ[i];\n sum = fmaf(d, d, sum);\n }\n\n sum = block_reduce_sum(sum);\n if (threadIdx.x == 0) partial[blockIdx.x] = sum;\n}\n\ntemplate<int BLOCK_SIZE>\n__global__ __launch_bounds__(BLOCK_SIZE, 1)\nvoid mse_final_kernel(const float* __restrict__ partial,\n float* __restrict__ out,\n int nblocks,\n float inv_n) {\n float sum = 0.0f;\n for (int i = threadIdx.x; i < nblocks; i += BLOCK_SIZE) {\n sum += partial[i];\n }\n sum = block_reduce_sum(sum);\n if (threadIdx.x == 0) out[0] = sum * inv_n;\n}\n\ntorch::Tensor mse_forward(torch::Tensor predictions, torch::Tensor targets) {\n TORCH_CHECK(predictions.is_cuda(), \"predictions must be a CUDA tensor\");\n TORCH_CHECK(targets.is_cuda(), \"targets must be a CUDA tensor\");\n TORCH_CHECK(predictions.scalar_type() == at::kFloat, \"only float32 is supported\");\n TORCH_CHECK(targets.scalar_type() == at::kFloat, \"only float32 is supported\");\n TORCH_CHECK(predictions.numel() == targets.numel(), \"input sizes must match\");\n\n auto pred_c = predictions.contiguous();\n auto targ_c = targets.contiguous();\n const int64_t n = pred_c.numel();\n auto out = torch::empty({}, pred_c.options());\n if (n == 0) {\n return out;\n }\n\n constexpr int BLOCK = 256;\n int device = pred_c.get_device();\n cudaDeviceProp prop;\n cudaGetDeviceProperties(&prop, device);\n // Enough blocks to cover the GPU well, while keeping final reduction tiny.\n int blocks = prop.multiProcessorCount * 8;\n int64_t min_blocks64 = (n + BLOCK - 1) / BLOCK;\n if (blocks > min_blocks64) blocks = (int)min_blocks64;\n if (blocks < 1) blocks = 1;\n // Cap partials to keep the second pass cheap. For the target problem this is\n // typically 800-1200 blocks depending on GPU.\n if (blocks > 4096) blocks = 4096;\n\n auto partial = torch::empty({blocks}, pred_c.options());\n cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n mse_partial_kernel<BLOCK><<<blocks, BLOCK, 0, stream>>>(\n pred_c.data_ptr<float>(), targ_c.data_ptr<float>(), partial.data_ptr<float>(), n);\n mse_final_kernel<1024><<<1, 1024, 0, stream>>>(\n partial.data_ptr<float>(), out.data_ptr<float>(), blocks, 1.0f / (float)n);\n return out;\n}\n\"\"\"\n\n_ext = load_inline(\n name=\"mse_mean_ext_v1\",\n cpp_sources=_cpp_src,\n cuda_sources=_cuda_src,\n functions=[\"mse_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):\n super(ModelNew, self).__init__()\n\n def forward(self, predictions, targets):\n return _ext.mse_forward(predictions, targets)\n"
}
result ← submit_kernel (ok)
oksubmit_kernel
submit_kernel PASSED: (5 / 5) correctness trials all passed.
Kernel runtime: 18.10 μs
Runtime stats: mean=18.10μs median=?μs std=72.50μs
Numerical precision: max_abs_err=1.49e-08 mean_abs_err=8.94e-09 max_rel_err=9.99e-08
Memory: 8225.0 MB peak (ref: 16417.0 MB, ratio: 0.501x)
Kernel launches: 2 (ref: 7, fusion ratio: 3.5)
Energy: 4396.9 mJ/run (ref: 1452.08 mJ, ratio: 0.3303x)
SOL score: 0.8702
final submitted kernel (142 lines, 4,854 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 mse_forward(torch::Tensor predictions, torch::Tensor targets);
"""
_cuda_src = r"""
#include <torch/extension.h>
#include <ATen/cuda/CUDAContext.h>
#include <cuda.h>
#include <cuda_runtime.h>
// Warp and block reductions for float sums.
__inline__ __device__ float warp_reduce_sum(float v) {
#pragma unroll
for (int offset = 16; offset > 0; offset >>= 1) {
v += __shfl_down_sync(0xffffffff, v, offset);
}
return v;
}
__inline__ __device__ float block_reduce_sum(float v) {
static __shared__ float shared[32];
int lane = threadIdx.x & 31;
int wid = threadIdx.x >> 5;
v = warp_reduce_sum(v);
if (lane == 0) shared[wid] = v;
__syncthreads();
v = (threadIdx.x < (blockDim.x >> 5)) ? shared[lane] : 0.0f;
if (wid == 0) v = warp_reduce_sum(v);
return v;
}
template<int BLOCK_SIZE>
__global__ __launch_bounds__(BLOCK_SIZE, 2)
void mse_partial_kernel(const float* __restrict__ pred,
const float* __restrict__ targ,
float* __restrict__ partial,
int64_t n) {
float sum = 0.0f;
int64_t tid = (int64_t)blockIdx.x * BLOCK_SIZE + threadIdx.x;
int64_t stride = (int64_t)gridDim.x * BLOCK_SIZE;
// Grid-stride loop with four independent accumulators improves ILP and reduces
// rounding error a little versus one long dependency chain.
float s0 = 0.0f, s1 = 0.0f, s2 = 0.0f, s3 = 0.0f;
int64_t i = tid;
int64_t step4 = stride * 4;
for (; i + 3 * stride < n; i += step4) {
float d0 = pred[i] - targ[i];
float d1 = pred[i + stride] - targ[i + stride];
float d2 = pred[i + 2 * stride] - targ[i + 2 * stride];
float d3 = pred[i + 3 * stride] - targ[i + 3 * stride];
s0 = fmaf(d0, d0, s0);
s1 = fmaf(d1, d1, s1);
s2 = fmaf(d2, d2, s2);
s3 = fmaf(d3, d3, s3);
}
sum = (s0 + s1) + (s2 + s3);
for (; i < n; i += stride) {
float d = pred[i] - targ[i];
sum = fmaf(d, d, sum);
}
sum = block_reduce_sum(sum);
if (threadIdx.x == 0) partial[blockIdx.x] = sum;
}
template<int BLOCK_SIZE>
__global__ __launch_bounds__(BLOCK_SIZE, 1)
void mse_final_kernel(const float* __restrict__ partial,
float* __restrict__ out,
int nblocks,
float inv_n) {
float sum = 0.0f;
for (int i = threadIdx.x; i < nblocks; i += BLOCK_SIZE) {
sum += partial[i];
}
sum = block_reduce_sum(sum);
if (threadIdx.x == 0) out[0] = sum * inv_n;
}
torch::Tensor mse_forward(torch::Tensor predictions, torch::Tensor targets) {
TORCH_CHECK(predictions.is_cuda(), "predictions must be a CUDA tensor");
TORCH_CHECK(targets.is_cuda(), "targets must be a CUDA tensor");
TORCH_CHECK(predictions.scalar_type() == at::kFloat, "only float32 is supported");
TORCH_CHECK(targets.scalar_type() == at::kFloat, "only float32 is supported");
TORCH_CHECK(predictions.numel() == targets.numel(), "input sizes must match");
auto pred_c = predictions.contiguous();
auto targ_c = targets.contiguous();
const int64_t n = pred_c.numel();
auto out = torch::empty({}, pred_c.options());
if (n == 0) {
return out;
}
constexpr int BLOCK = 256;
int device = pred_c.get_device();
cudaDeviceProp prop;
cudaGetDeviceProperties(&prop, device);
// Enough blocks to cover the GPU well, while keeping final reduction tiny.
int blocks = prop.multiProcessorCount * 8;
int64_t min_blocks64 = (n + BLOCK - 1) / BLOCK;
if (blocks > min_blocks64) blocks = (int)min_blocks64;
if (blocks < 1) blocks = 1;
// Cap partials to keep the second pass cheap. For the target problem this is
// typically 800-1200 blocks depending on GPU.
if (blocks > 4096) blocks = 4096;
auto partial = torch::empty({blocks}, pred_c.options());
cudaStream_t stream = at::cuda::getCurrentCUDAStream();
mse_partial_kernel<BLOCK><<<blocks, BLOCK, 0, stream>>>(
pred_c.data_ptr<float>(), targ_c.data_ptr<float>(), partial.data_ptr<float>(), n);
mse_final_kernel<1024><<<1, 1024, 0, stream>>>(
partial.data_ptr<float>(), out.data_ptr<float>(), blocks, 1.0f / (float)n);
return out;
}
"""
_ext = load_inline(
name="mse_mean_ext_v1",
cpp_sources=_cpp_src,
cuda_sources=_cuda_src,
functions=["mse_forward"],
extra_cflags=["-O3"],
extra_cuda_cflags=["-O3", "--use_fast_math"],
with_cuda=True,
verbose=False,
)
class ModelNew(nn.Module):
def __init__(self):
super(ModelNew, self).__init__()
def forward(self, predictions, targets):
return _ext.mse_forward(predictions, targets)