gpt-5.5-priority · original · l123_single_gpt
correct 86_Matmul_Divide_GELU.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
2.75 μs
ref runtime
2.73 μs
speedup
0.99x
started
2026-05-05T00:31:02Z
timingkernelbench.timing
kernel runtime
2.75 μs
ref runtime
2.73 μs
speedup
0.99x
kernel mean / median / std
2.75 / — / 0.00 μs
ref mean / median / std
2.73 / — / 0.00 μs
numerical precisioneval_kernel_against_ref
max abs error
2.24e-08
mean abs error
3.87e-10
max rel error
0.07
mean rel error
3.47e-08
gpu memoryextended_metrics.measure_memory
kernel peak
609.06 MB
ref peak
641.06 MB
kernel / ref
0.95x
kernel launches / fusiontorch.profiler
kernel launches
1
ref launches
7
fusion ratio (ref / kernel)
7.00x
total cuda time
22 μs
energyNVML (pynvml)
kernel mJ / run
230.68
ref mJ / run
921.90
ratio (ref / kernel)
4.00x
avg power
83.88 W
ref avg power
337.62 W
SOL (speed-of-light)nsight_hardware_counters
SOL score (0–1)
0.505
dram util %
50.5%
compute util %
7.5%
bottleneck
memory-bound
dominant pipe
tensor-core
arithmetic intensity
—
ridge point
295.22
roofline / occupancynsight_hardware_counters
dram bandwidth
1691.51 GB/s
dram util %
50.5%
fp32 tflops
—
fp32 util %
—
fp16 tflops
—
occupancy %
87.7%
bottleneck
memory-bound
dominant pipe %
7.5%
arithmetic intensity
—
ridge point
295.22
L1 hit %
—
L2 hit %
145.4%
ld sectors / req
8.00
st sectors / req
—
regs / thread
271
smem / block
35328 B
block size
384
peak bw
3350.00 GB/s
peak fp32 tflops
67.00
peak fp16 tflops
1979.00
warp stalls (top 5)nsight
long_scoreboard
1497.0%
short_scoreboard
186.2%
mio_throttle
52.9%
reference PyTorch source (the prompt the agent sees)
import torch
import torch.nn as nn

class Model(nn.Module):
    """
    A model that performs a matrix multiplication, divides by a scalar, and applies GELU activation.
    """
    def __init__(self, input_size, output_size, divisor):
        super(Model, self).__init__()
        self.linear = nn.Linear(input_size, output_size)
        self.divisor = divisor

    def forward(self, x):
        """
        Args:
            x (torch.Tensor): Input tensor of shape (batch_size, input_size).
        Returns:
            torch.Tensor: Output tensor of shape (batch_size, output_size).
        """
        x = self.linear(x)
        x = x / self.divisor
        x = torch.nn.functional.gelu(x)
        return x

batch_size = 1024
input_size = 8192
output_size = 8192
divisor = 10.0

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

def get_init_inputs():
    return [input_size, output_size, divisor]
turn 0 · FINAL · 1 tool call(s) · 69.1s turn · 69.1s LLM · 7,756 chars (click to expand)
turn 069.1s turn · 69.1s LLM · 0.0s tools · FINAL
reasoning
**Considering CUDA implementation** I'm thinking about whether the implementation in cuda_sources should be exclusive. It seems like functions might only bind to a compiled function if they're declared in cpp_sources. It might be better to define the C++ wrapper in cpp_sources, while calling the launcher that's been declared and implemented in CUDA. But, hmm, I wonder if the cuBLAS host code can actually be in cpp_sources too. It's a bit tricky to navigate! **Including libraries for functionality** I realize I need to include the `torch/extension.h` as well. This makes sense since it's likely essential for the functionality I’m working on. I wonder if there’s anything else I might have overlooked. Maybe there are additional libraries or dependencies that could improve my project. But for now, I’ll focus on ensuring I’ve got this crucial header in place, as it seems like a foundational step to take. **Evaluating matrix calculations** I’m considering how to speed up memory passes, noticing that one is faster than two. Using cublasSgemm with a specific alpha and beta could streamline the process. The formula involves matrix multiplication adjusted by a divisor and then adding a scaled bias. I see potential for saving operations in the kernel, but we need to ensure the bias is computed properly for each element if it’s not pre-scaled. Finally, including a constant divisor keeps things consistent.
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#include <ATen/cuda/CUDAContext.h>\n#include <ATen/Context.h>\n#include <cublas_v2.h>\n\nvoid gelu_bias_div_launcher(torch::Tensor y, torch::Tensor bias, double inv_divisor);\n\ntorch::Tensor linear_div_gelu(torch::Tensor x, torch::Tensor weight, torch::Tensor bias, double divisor) {\n TORCH_CHECK(x.is_cuda(), \"x must be CUDA\");\n TORCH_CHECK(weight.is_cuda(), \"weight must be CUDA\");\n TORCH_CHECK(bias.is_cuda(), \"bias must be CUDA\");\n TORCH_CHECK(x.scalar_type() == torch::kFloat32, \"only float32 is supported\");\n TORCH_CHECK(weight.scalar_type() == torch::kFloat32, \"only float32 is supported\");\n TORCH_CHECK(bias.scalar_type() == torch::kFloat32, \"only float32 is supported\");\n TORCH_CHECK(x.is_contiguous(), \"x must be contiguous\");\n TORCH_CHECK(weight.is_contiguous(), \"weight must be contiguous\");\n TORCH_CHECK(bias.is_contiguous(), \"bias must be contiguous\");\n TORCH_CHECK(x.dim() == 2 && weight.dim() == 2 && bias.dim() == 1, \"bad tensor ranks\");\n\n const int64_t M64 = x.size(0);\n const int64_t K64 = x.size(1);\n const int64_t N64 = weight.size(0);\n TORCH_CHECK(weight.size(1) == K64, \"incompatible dimensions\");\n TORCH_CHECK(bias.size(0) == N64, \"incompatible bias\");\n TORCH_CHECK(M64 <= INT_MAX && N64 <= INT_MAX && K64 <= INT_MAX, \"dimensions too large\");\n const int M = static_cast<int>(M64);\n const int N = static_cast<int>(N64);\n const int K = static_cast<int>(K64);\n\n auto y = torch::empty({M64, N64}, x.options());\n\n cublasHandle_t handle = at::cuda::getCurrentCUDABlasHandle();\n cublasSetStream(handle, at::cuda::getCurrentCUDAStream());\n\n // Match PyTorch's TF32 setting for fp32 matmul as closely as possible.\n#if defined(CUBLAS_TF32_TENSOR_OP_MATH) && defined(CUBLAS_PEDANTIC_MATH)\n cublasSetMathMode(handle, at::globalContext().allowTF32CuBLAS() ? CUBLAS_TF32_TENSOR_OP_MATH : CUBLAS_PEDANTIC_MATH);\n#elif defined(CUBLAS_PEDANTIC_MATH)\n cublasSetMathMode(handle, CUBLAS_PEDANTIC_MATH);\n#else\n cublasSetMathMode(handle, CUBLAS_DEFAULT_MATH);\n#endif\n\n // Row-major y[M,N] = (x[M,K] * weight[N,K]^T) / divisor.\n // Interpreting row-major buffers as column-major gives y^T[N,M] = weight * x^T.\n const float alpha = static_cast<float>(1.0 / divisor);\n const float beta = 0.0f;\n cublasStatus_t st = cublasSgemm(\n handle,\n CUBLAS_OP_T, CUBLAS_OP_N,\n N, M, K,\n &alpha,\n weight.data_ptr<float>(), K,\n x.data_ptr<float>(), K,\n &beta,\n y.data_ptr<float>(), N);\n TORCH_CHECK(st == CUBLAS_STATUS_SUCCESS, \"cublasSgemm failed with status \", (int)st);\n\n gelu_bias_div_launcher(y, bias, 1.0 / divisor);\n return y;\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_runtime.h>\n#include <math_constants.h>\n\n__global__ void gelu_bias_div_kernel(float* __restrict__ y,\n const float* __restrict__ bias,\n int M, int N, float inv_divisor) {\n const int col = blockIdx.x * blockDim.x + threadIdx.x;\n const int row = blockIdx.y;\n if (col < N && row < M) {\n const int idx = row * N + col;\n float v = y[idx] + bias[col] * inv_divisor;\n // Exact GELU: 0.5*x*(1 + erf(x/sqrt(2))).\n float r = 0.5f * v * (1.0f + erff(v * 0.70710678118654752440f));\n y[idx] = r;\n }\n}\n\nvoid gelu_bias_div_launcher(torch::Tensor y, torch::Tensor bias, double inv_divisor) {\n const int M = static_cast<int>(y.size(0));\n const int N = static_cast<int>(y.size(1));\n const dim3 block(256);\n const dim3 grid((N + block.x - 1) / block.x, M);\n gelu_bias_div_kernel<<<grid, block, 0, at::cuda::getCurrentCUDAStream()>>>(\n y.data_ptr<float>(), bias.data_ptr<float>(), M, N, static_cast<float>(inv_divisor));\n C10_CUDA_KERNEL_LAUNCH_CHECK();\n}\n'''\n\n_ext = load_inline(\n name='linear_div_gelu_fused_ext_v1',\n cpp_sources=[_cpp_src],\n cuda_sources=[_cuda_src],\n functions=['linear_div_gelu'],\n extra_cflags=['-O3'],\n extra_cuda_cflags=['-O3'],\n extra_ldflags=['-lcublas'],\n with_cuda=True,\n verbose=False,\n)\n\nclass ModelNew(nn.Module):\n def __init__(self, input_size, output_size, divisor):\n super(ModelNew, self).__init__()\n self.linear = nn.Linear(input_size, output_size)\n self.divisor = divisor\n\n def forward(self, x):\n if not x.is_contiguous():\n x = x.contiguous()\n return _ext.linear_div_gelu(x, self.linear.weight, self.linear.bias, float(self.divisor))\n" }
result ← submit_kernel (ok)
oksubmit_kernel submit_kernel PASSED: (5 / 5) correctness trials all passed. Kernel runtime: 2.75 μs Runtime stats: mean=2.75μs median=?μs std=0.00μs Numerical precision: max_abs_err=2.24e-08 mean_abs_err=3.87e-10 max_rel_err=6.56e-02 Memory: 609.06 MB peak (ref: 641.06 MB, ratio: 0.9501x) Kernel launches: 1 (ref: 7, fusion ratio: 7.0) Energy: 230.68 mJ/run (ref: 921.9 mJ, ratio: 3.9964x) SOL score: 0.5049
final submitted kernel (123 lines, 4,714 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>
#include <ATen/cuda/CUDAContext.h>
#include <ATen/Context.h>
#include <cublas_v2.h>

void gelu_bias_div_launcher(torch::Tensor y, torch::Tensor bias, double inv_divisor);

torch::Tensor linear_div_gelu(torch::Tensor x, torch::Tensor weight, torch::Tensor bias, double divisor) {
    TORCH_CHECK(x.is_cuda(), "x must be CUDA");
    TORCH_CHECK(weight.is_cuda(), "weight must be CUDA");
    TORCH_CHECK(bias.is_cuda(), "bias must be CUDA");
    TORCH_CHECK(x.scalar_type() == torch::kFloat32, "only float32 is supported");
    TORCH_CHECK(weight.scalar_type() == torch::kFloat32, "only float32 is supported");
    TORCH_CHECK(bias.scalar_type() == torch::kFloat32, "only float32 is supported");
    TORCH_CHECK(x.is_contiguous(), "x must be contiguous");
    TORCH_CHECK(weight.is_contiguous(), "weight must be contiguous");
    TORCH_CHECK(bias.is_contiguous(), "bias must be contiguous");
    TORCH_CHECK(x.dim() == 2 && weight.dim() == 2 && bias.dim() == 1, "bad tensor ranks");

    const int64_t M64 = x.size(0);
    const int64_t K64 = x.size(1);
    const int64_t N64 = weight.size(0);
    TORCH_CHECK(weight.size(1) == K64, "incompatible dimensions");
    TORCH_CHECK(bias.size(0) == N64, "incompatible bias");
    TORCH_CHECK(M64 <= INT_MAX && N64 <= INT_MAX && K64 <= INT_MAX, "dimensions too large");
    const int M = static_cast<int>(M64);
    const int N = static_cast<int>(N64);
    const int K = static_cast<int>(K64);

    auto y = torch::empty({M64, N64}, x.options());

    cublasHandle_t handle = at::cuda::getCurrentCUDABlasHandle();
    cublasSetStream(handle, at::cuda::getCurrentCUDAStream());

    // Match PyTorch's TF32 setting for fp32 matmul as closely as possible.
#if defined(CUBLAS_TF32_TENSOR_OP_MATH) && defined(CUBLAS_PEDANTIC_MATH)
    cublasSetMathMode(handle, at::globalContext().allowTF32CuBLAS() ? CUBLAS_TF32_TENSOR_OP_MATH : CUBLAS_PEDANTIC_MATH);
#elif defined(CUBLAS_PEDANTIC_MATH)
    cublasSetMathMode(handle, CUBLAS_PEDANTIC_MATH);
#else
    cublasSetMathMode(handle, CUBLAS_DEFAULT_MATH);
#endif

    // Row-major y[M,N] = (x[M,K] * weight[N,K]^T) / divisor.
    // Interpreting row-major buffers as column-major gives y^T[N,M] = weight * x^T.
    const float alpha = static_cast<float>(1.0 / divisor);
    const float beta = 0.0f;
    cublasStatus_t st = cublasSgemm(
        handle,
        CUBLAS_OP_T, CUBLAS_OP_N,
        N, M, K,
        &alpha,
        weight.data_ptr<float>(), K,
        x.data_ptr<float>(), K,
        &beta,
        y.data_ptr<float>(), N);
    TORCH_CHECK(st == CUBLAS_STATUS_SUCCESS, "cublasSgemm failed with status ", (int)st);

    gelu_bias_div_launcher(y, bias, 1.0 / divisor);
    return y;
}
'''

_cuda_src = r'''
#include <torch/extension.h>
#include <ATen/cuda/CUDAContext.h>
#include <c10/cuda/CUDAException.h>
#include <cuda_runtime.h>
#include <math_constants.h>

__global__ void gelu_bias_div_kernel(float* __restrict__ y,
                                     const float* __restrict__ bias,
                                     int M, int N, float inv_divisor) {
    const int col = blockIdx.x * blockDim.x + threadIdx.x;
    const int row = blockIdx.y;
    if (col < N && row < M) {
        const int idx = row * N + col;
        float v = y[idx] + bias[col] * inv_divisor;
        // Exact GELU: 0.5*x*(1 + erf(x/sqrt(2))).
        float r = 0.5f * v * (1.0f + erff(v * 0.70710678118654752440f));
        y[idx] = r;
    }
}

void gelu_bias_div_launcher(torch::Tensor y, torch::Tensor bias, double inv_divisor) {
    const int M = static_cast<int>(y.size(0));
    const int N = static_cast<int>(y.size(1));
    const dim3 block(256);
    const dim3 grid((N + block.x - 1) / block.x, M);
    gelu_bias_div_kernel<<<grid, block, 0, at::cuda::getCurrentCUDAStream()>>>(
        y.data_ptr<float>(), bias.data_ptr<float>(), M, N, static_cast<float>(inv_divisor));
    C10_CUDA_KERNEL_LAUNCH_CHECK();
}
'''

_ext = load_inline(
    name='linear_div_gelu_fused_ext_v1',
    cpp_sources=[_cpp_src],
    cuda_sources=[_cuda_src],
    functions=['linear_div_gelu'],
    extra_cflags=['-O3'],
    extra_cuda_cflags=['-O3'],
    extra_ldflags=['-lcublas'],
    with_cuda=True,
    verbose=False,
)

class ModelNew(nn.Module):
    def __init__(self, input_size, output_size, divisor):
        super(ModelNew, self).__init__()
        self.linear = nn.Linear(input_size, output_size)
        self.divisor = divisor

    def forward(self, x):
        if not x.is_contiguous():
            x = x.contiguous()
        return _ext.linear_div_gelu(x, self.linear.weight, self.linear.bias, float(self.divisor))