gpt-5.5-priority · original · l123_single_gpt
correct 40_Matmul_Scaling_ResidualAdd.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
11.20 μs
ref runtime
11.50 μs
speedup
1.03x
started
2026-05-05T00:46:00Z
timingkernelbench.timing
kernel runtime
11.20 μs
ref runtime
11.50 μs
speedup
1.03x
kernel mean / median / std
11.20 / — / 0.50 μs
ref mean / median / std
11.50 / — / 0.23 μs
numerical precisioneval_kernel_against_ref
max abs error
2.38e-07
mean abs error
9.18e-09
max rel error
0.04
mean rel error
2.44e-08
gpu memoryextended_metrics.measure_memory
kernel peak
673.03 MB
ref peak
1185.03 MB
kernel / ref
0.57x
kernel launches / fusiontorch.profiler
kernel launches
1
ref launches
12
fusion ratio (ref / kernel)
12.00x
total cuda time
24 μs
energyNVML (pynvml)
kernel mJ / run
1441.56
ref mJ / run
1922.28
ratio (ref / kernel)
1.33x
avg power
129.89 W
ref avg power
160.76 W
SOL (speed-of-light)nsight_hardware_counters
SOL score (0–1)
1.000
dram util %
75.3%
compute util %
100.0%
bottleneck
compute-bound
dominant pipe
FP32
arithmetic intensity
—
ridge point
20.00
roofline / occupancynsight_hardware_counters
dram bandwidth
2524.14 GB/s
dram util %
75.3%
fp32 tflops
—
fp32 util %
—
fp16 tflops
—
occupancy %
102.1%
bottleneck
compute-bound
dominant pipe %
111.6%
arithmetic intensity
—
ridge point
20.00
L1 hit %
—
L2 hit %
173.6%
ld sectors / req
8.00
st sectors / req
—
regs / thread
280
smem / block
35840 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
short_scoreboard
140.3%
mio_throttle
20.9%
long_scoreboard
13.2%
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, scaling, and residual addition.

    Args:
        in_features (int): Number of input features.
        out_features (int): Number of output features.
        scaling_factor (float): Scaling factor to apply after matrix multiplication.
    """
    def __init__(self, in_features, out_features, scaling_factor):
        super(Model, self).__init__()
        self.matmul = nn.Linear(in_features, out_features)
        self.scaling_factor = scaling_factor

    def forward(self, x):
        """
        Forward pass of the model.

        Args:
            x (torch.Tensor): Input tensor of shape (batch_size, in_features).

        Returns:
            torch.Tensor: Output tensor of shape (batch_size, out_features).
        """
        x = self.matmul(x)
        original_x = x.clone().detach()
        x = x * self.scaling_factor
        x = x + original_x
        return x

batch_size = 16384
in_features = 4096
out_features = 4096
scaling_factor = 0.5

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

def get_init_inputs():
    return [in_features, out_features, scaling_factor]
turn 0 · FINAL · 1 tool call(s) · 72.7s turn · 72.7s LLM · 8,251 chars (click to expand)
turn 072.7s turn · 72.7s LLM · 0.0s tools · FINAL
reasoning
**Crafting a Python Model** I’m working on a Python model that needs to replace the “Linear + scale/add” process. The output should follow a specific format: linear(x) * (scaling + 1). However, the issue is that the original clone detaches the same values without gradients, making it a forward-only operation. I need to ensure the model accepts parameters, probably by using nn.Module with nn.Linear. Evaluating the model involves copying the state_dict, and I’m considering the efficiency of operations like GEMM and whether to use custom CUDA tasks. I also wonder if torch operations would be sensible here. **Developing a Custom CUDA Solution** I'm considering compiling an extension with a host wrapper using at::linear, but I realize that routes to PyTorch, which isn't ideal. I need to think about a custom CUDA kernel for the epilogue after cublas. The output is structured as output = alpha * (XW^T + b), so I want to utilize cublasSgemm for alpha * XW^T and then add the custom kernel for bias. By using cublas with beta initialization for the bias, I could efficiently manage the operations. I also want to leverage row-major tricks in PyTorch for optimization. **Evaluating PyTorch and TF32** I'm considering how PyTorch handles TF32, and how that might differ from full fp32. There's a chance the tolerance is loose, around 1e-4 at specific values. Many evaluations probably use TF32 by default, especially for convolutions. In recent versions, the default for matmul can be set to not allow TF32. I also wonder if full fp32 cublas operations could be slower than PyTorch. Plus, I need to think about handling bias, particularly how it's managed in the linear function.
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 fused_linear_scale_residual_forward(torch::Tensor x,\n torch::Tensor weight,\n torch::Tensor bias,\n double scaling_factor);\n\"\"\"\n\n_cuda_src = r\"\"\"\n#include <torch/extension.h>\n#include <ATen/cuda/CUDAContext.h>\n#include <ATen/Context.h>\n#include <c10/cuda/CUDAGuard.h>\n#include <cuda_runtime.h>\n#include <cublas_v2.h>\n\n#define CHECK_CUBLAS(call) do { \\\n cublasStatus_t _status = (call); \\\n TORCH_CHECK(_status == CUBLAS_STATUS_SUCCESS, \\\n \"cuBLAS error: \", static_cast<int>(_status)); \\\n} while (0)\n\n__global__ void fill_scaled_bias_kernel(float* __restrict__ out,\n const float* __restrict__ bias,\n int64_t total,\n int64_t n,\n float scale) {\n int64_t idx = (int64_t)blockIdx.x * blockDim.x + threadIdx.x;\n int64_t stride = (int64_t)blockDim.x * gridDim.x;\n for (; idx < total; idx += stride) {\n out[idx] = scale * bias[idx % n];\n }\n}\n\ntorch::Tensor fused_linear_scale_residual_forward(torch::Tensor x,\n torch::Tensor weight,\n torch::Tensor bias,\n double scaling_factor) {\n TORCH_CHECK(x.is_cuda() && weight.is_cuda() && bias.is_cuda(), \"all tensors must be CUDA tensors\");\n TORCH_CHECK(x.scalar_type() == at::kFloat && weight.scalar_type() == at::kFloat && bias.scalar_type() == at::kFloat,\n \"only float32 tensors are supported\");\n TORCH_CHECK(x.is_contiguous() && weight.is_contiguous() && bias.is_contiguous(),\n \"all tensors must be contiguous\");\n TORCH_CHECK(x.dim() == 2 && weight.dim() == 2 && bias.dim() == 1, \"bad tensor dimensions\");\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 && bias.size(0) == N64, \"incompatible shapes\");\n TORCH_CHECK(M64 <= INT_MAX && N64 <= INT_MAX && K64 <= INT_MAX, \"matrix too large for cuBLAS int API\");\n\n c10::cuda::CUDAGuard device_guard(x.device());\n auto out = torch::empty({M64, N64}, x.options());\n\n const float gamma = static_cast<float>(1.0 + scaling_factor);\n const int64_t total = M64 * N64;\n cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n\n const int threads = 256;\n int blocks = (int)((total + threads - 1) / threads);\n if (blocks > 65535) blocks = 65535;\n fill_scaled_bias_kernel<<<blocks, threads, 0, stream>>>(\n out.data_ptr<float>(), bias.data_ptr<float>(), total, N64, gamma);\n C10_CUDA_KERNEL_LAUNCH_CHECK();\n\n cublasHandle_t handle = at::cuda::getCurrentCUDABlasHandle();\n\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 const float alpha = gamma;\n const float beta = 1.0f;\n\n // Row-major C[M,N] is viewed as column-major C^T[N,M].\n // Compute C^T = gamma * W[N,K] * X^T[K,M] + scaled_bias, using W and X storage directly.\n cublasComputeType_t compute_type = at::globalContext().allowTF32CuBLAS()\n ? CUBLAS_COMPUTE_32F_FAST_TF32\n : CUBLAS_COMPUTE_32F_PEDANTIC;\n\n CHECK_CUBLAS(cublasGemmEx(handle,\n CUBLAS_OP_T, CUBLAS_OP_N,\n N, M, K,\n &alpha,\n weight.data_ptr<float>(), CUDA_R_32F, K,\n x.data_ptr<float>(), CUDA_R_32F, K,\n &beta,\n out.data_ptr<float>(), CUDA_R_32F, N,\n compute_type,\n CUBLAS_GEMM_DEFAULT));\n return out;\n}\n\"\"\"\n\n_ext = load_inline(\n name=\"fused_linear_scale_residual_ext_v1\",\n cpp_sources=_cpp_src,\n cuda_sources=_cuda_src,\n functions=[\"fused_linear_scale_residual_forward\"],\n extra_cflags=[\"-O3\"],\n extra_cuda_cflags=[\"-O3\"],\n extra_ldflags=[\"-lcublas\"],\n with_cuda=True,\n verbose=False,\n)\n\n\nclass ModelNew(nn.Module):\n def __init__(self, in_features, out_features, scaling_factor):\n super(ModelNew, self).__init__()\n self.matmul = nn.Linear(in_features, out_features)\n self.scaling_factor = scaling_factor\n\n def forward(self, x):\n return _ext.fused_linear_scale_residual_forward(\n x.contiguous(),\n self.matmul.weight.contiguous(),\n self.matmul.bias.contiguous(),\n float(self.scaling_factor),\n )\n" }
result ← submit_kernel (ok)
oksubmit_kernel submit_kernel PASSED: (5 / 5) correctness trials all passed. Kernel runtime: 11.20 μs Runtime stats: mean=11.20μs median=?μs std=0.50μs Numerical precision: max_abs_err=2.38e-07 mean_abs_err=9.18e-09 max_rel_err=4.17e-02 Memory: 673.03 MB peak (ref: 1185.03 MB, ratio: 0.5679x) Kernel launches: 1 (ref: 12, fusion ratio: 12.0) Energy: 1441.56 mJ/run (ref: 1922.28 mJ, ratio: 1.3335x) SOL score: 1.0
final submitted kernel (125 lines, 4,982 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 fused_linear_scale_residual_forward(torch::Tensor x,
                                                  torch::Tensor weight,
                                                  torch::Tensor bias,
                                                  double scaling_factor);
"""

_cuda_src = r"""
#include <torch/extension.h>
#include <ATen/cuda/CUDAContext.h>
#include <ATen/Context.h>
#include <c10/cuda/CUDAGuard.h>
#include <cuda_runtime.h>
#include <cublas_v2.h>

#define CHECK_CUBLAS(call) do {                                      \
    cublasStatus_t _status = (call);                                 \
    TORCH_CHECK(_status == CUBLAS_STATUS_SUCCESS,                    \
                "cuBLAS error: ", static_cast<int>(_status));        \
} while (0)

__global__ void fill_scaled_bias_kernel(float* __restrict__ out,
                                         const float* __restrict__ bias,
                                         int64_t total,
                                         int64_t n,
                                         float scale) {
    int64_t idx = (int64_t)blockIdx.x * blockDim.x + threadIdx.x;
    int64_t stride = (int64_t)blockDim.x * gridDim.x;
    for (; idx < total; idx += stride) {
        out[idx] = scale * bias[idx % n];
    }
}

torch::Tensor fused_linear_scale_residual_forward(torch::Tensor x,
                                                  torch::Tensor weight,
                                                  torch::Tensor bias,
                                                  double scaling_factor) {
    TORCH_CHECK(x.is_cuda() && weight.is_cuda() && bias.is_cuda(), "all tensors must be CUDA tensors");
    TORCH_CHECK(x.scalar_type() == at::kFloat && weight.scalar_type() == at::kFloat && bias.scalar_type() == at::kFloat,
                "only float32 tensors are supported");
    TORCH_CHECK(x.is_contiguous() && weight.is_contiguous() && bias.is_contiguous(),
                "all tensors must be contiguous");
    TORCH_CHECK(x.dim() == 2 && weight.dim() == 2 && bias.dim() == 1, "bad tensor dimensions");

    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 && bias.size(0) == N64, "incompatible shapes");
    TORCH_CHECK(M64 <= INT_MAX && N64 <= INT_MAX && K64 <= INT_MAX, "matrix too large for cuBLAS int API");

    c10::cuda::CUDAGuard device_guard(x.device());
    auto out = torch::empty({M64, N64}, x.options());

    const float gamma = static_cast<float>(1.0 + scaling_factor);
    const int64_t total = M64 * N64;
    cudaStream_t stream = at::cuda::getCurrentCUDAStream();

    const int threads = 256;
    int blocks = (int)((total + threads - 1) / threads);
    if (blocks > 65535) blocks = 65535;
    fill_scaled_bias_kernel<<<blocks, threads, 0, stream>>>(
        out.data_ptr<float>(), bias.data_ptr<float>(), total, N64, gamma);
    C10_CUDA_KERNEL_LAUNCH_CHECK();

    cublasHandle_t handle = at::cuda::getCurrentCUDABlasHandle();

    const int M = static_cast<int>(M64);
    const int N = static_cast<int>(N64);
    const int K = static_cast<int>(K64);
    const float alpha = gamma;
    const float beta = 1.0f;

    // Row-major C[M,N] is viewed as column-major C^T[N,M].
    // Compute C^T = gamma * W[N,K] * X^T[K,M] + scaled_bias, using W and X storage directly.
    cublasComputeType_t compute_type = at::globalContext().allowTF32CuBLAS()
        ? CUBLAS_COMPUTE_32F_FAST_TF32
        : CUBLAS_COMPUTE_32F_PEDANTIC;

    CHECK_CUBLAS(cublasGemmEx(handle,
                              CUBLAS_OP_T, CUBLAS_OP_N,
                              N, M, K,
                              &alpha,
                              weight.data_ptr<float>(), CUDA_R_32F, K,
                              x.data_ptr<float>(), CUDA_R_32F, K,
                              &beta,
                              out.data_ptr<float>(), CUDA_R_32F, N,
                              compute_type,
                              CUBLAS_GEMM_DEFAULT));
    return out;
}
"""

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


class ModelNew(nn.Module):
    def __init__(self, in_features, out_features, scaling_factor):
        super(ModelNew, self).__init__()
        self.matmul = nn.Linear(in_features, out_features)
        self.scaling_factor = scaling_factor

    def forward(self, x):
        return _ext.fused_linear_scale_residual_forward(
            x.contiguous(),
            self.matmul.weight.contiguous(),
            self.matmul.bias.contiguous(),
            float(self.scaling_factor),
        )