correct
18_Matmul_with_transposed_both.py
started
2026-05-05T00:38:55Z
timingkernelbench.timing
kernel mean / median / std
2.77 / — / 0.00 μs
ref mean / median / std
0.32 / — / 7.01e-04 μs
numerical precisioneval_kernel_against_ref
gpu memoryextended_metrics.measure_memory
kernel launches / fusiontorch.profiler
fusion ratio (ref / kernel)
8.00x
SOL (speed-of-light)nsight_hardware_counters
roofline / occupancynsight_hardware_counters
dram bandwidth
116.73 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 a single matrix multiplication (C = A * B)
"""
def __init__(self):
super(Model, self).__init__()
def forward(self, A: torch.Tensor, B: torch.Tensor) -> torch.Tensor:
"""
Performs matrix multiplication.
Args:
A: Input tensor of shape (M, K).
B: Input tensor of shape (K, N).
Returns:
Output tensor of shape (M, N).
"""
return torch.matmul(A.T, B.T)
M = 1024 * 2
K = 4096 * 2
N = 2048 * 2
def get_inputs():
A = torch.rand(K, M)
B = torch.rand(N, K)
return [A, B]
def get_init_inputs():
return [] # No special initialization inputs neededturn 044.1s turn · 44.1s LLM · 0.0s tools · FINAL
reasoning
**Considering PyTorch settings**
I'm thinking that the default setting for `matmul.allow_tf32` in recent PyTorch versions could be False. To be safe, I can set `cublasSetMathMode` to use CUBLAS_DEFAULT_MATH. With PyTorch 2, it seems like the default might be False too. Also, since large GEMM can be slower without tensor cores, I should consider the potential benefits of TF32. However, it looks like using `cublasGemmEx` might lead to output errors when comparing to the reference FP32. I need to ensure I have complete Python for this implementation.
**Evaluating PyTorch settings**
I'm diving into the complexities of PyTorch settings and its C++ wrappers, which seem a bit complicated. I'm concerned about comparing outputs of torch.matmul(A.T, B.T) with specific tolerances. I'm curious if cuBLAS handles operations differently based on dimensions. Also, I wonder if PyTorch recognizes it can utilize cuBLAS efficiently. There’s the potential to speed things up with cublasLt, but I'm pondering whether there are issues with memory allocation or if it could be addressed at compile time. Let's finalize this!
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 <c10/cuda/CUDAGuard.h>\n#include <cublas_v2.h>\n\n#define CUBLAS_CHECK(cmd) do { \\\n cublasStatus_t status = (cmd); \\\n TORCH_CHECK(status == CUBLAS_STATUS_SUCCESS, \\\n \"cuBLAS error, status=\", (int)status); \\\n} while (0)\n\ntorch::Tensor matmul_at_bt(torch::Tensor A, torch::Tensor B) {\n TORCH_CHECK(A.is_cuda(), \"A must be a CUDA tensor\");\n TORCH_CHECK(B.is_cuda(), \"B must be a CUDA tensor\");\n TORCH_CHECK(A.scalar_type() == at::kFloat, \"A must be float32\");\n TORCH_CHECK(B.scalar_type() == at::kFloat, \"B must be float32\");\n TORCH_CHECK(A.is_contiguous(), \"A must be contiguous\");\n TORCH_CHECK(B.is_contiguous(), \"B must be contiguous\");\n TORCH_CHECK(A.dim() == 2 && B.dim() == 2, \"inputs must be 2D\");\n TORCH_CHECK(A.size(0) == B.size(1), \"inner dimension mismatch\");\n\n const int64_t K64 = A.size(0);\n const int64_t M64 = A.size(1);\n const int64_t N64 = B.size(0);\n TORCH_CHECK(K64 <= INT_MAX && M64 <= INT_MAX && N64 <= INT_MAX, \"matrix too large\");\n\n const int K = static_cast<int>(K64);\n const int M = static_cast<int>(M64);\n const int N = static_cast<int>(N64);\n\n c10::cuda::CUDAGuard device_guard(A.device());\n auto C = torch::empty({M64, N64}, A.options());\n\n cublasHandle_t handle = at::cuda::getCurrentCUDABlasHandle();\n CUBLAS_CHECK(cublasSetStream(handle, at::cuda::getCurrentCUDAStream()));\n // Match fp32 PyTorch matmul numerics (no TF32 tensor-core approximation).\n CUBLAS_CHECK(cublasSetMathMode(handle, CUBLAS_DEFAULT_MATH));\n\n const float alpha = 1.0f;\n const float beta = 0.0f;\n\n // Row-major A is KxM and row-major B is NxK. Interpreted as column-major,\n // A stores an MxK matrix A_col with A_col[m,k] = A[k,m], and B stores a\n // KxN matrix B_col with B_col[k,n] = B[n,k]. The row-major output C (MxN)\n // is the same memory as a column-major NxM matrix C_col = C^T, so compute:\n // C_col = B_col^T * A_col^T.\n CUBLAS_CHECK(cublasSgemm(handle,\n CUBLAS_OP_T, CUBLAS_OP_T,\n N, M, K,\n &alpha,\n B.data_ptr<float>(), K,\n A.data_ptr<float>(), M,\n &beta,\n C.data_ptr<float>(), N));\n return C;\n}\n'''\n\n_ext = load_inline(\n name='matmul_at_bt_cublas_ext_v1',\n cpp_sources=_cpp_src,\n cuda_sources='',\n functions=['matmul_at_bt'],\n with_cuda=True,\n extra_cflags=['-O3'],\n extra_cuda_cflags=['-O3'],\n extra_ldflags=['-lcublas'],\n verbose=False,\n)\n\nclass ModelNew(nn.Module):\n def __init__(self):\n super(ModelNew, self).__init__()\n\n def forward(self, A: torch.Tensor, B: torch.Tensor) -> torch.Tensor:\n return _ext.matmul_at_bt(A, B)\n"
}
result ← submit_kernel (ok)
oksubmit_kernel
submit_kernel PASSED: (5 / 5) correctness trials all passed.
Kernel runtime: 2.77 μs
Runtime stats: mean=2.77μs median=?μs std=0.00μs
Numerical precision: max_abs_err=1.62e-01 mean_abs_err=1.13e-01 max_rel_err=7.90e-05
Memory: 257.0 MB peak (ref: 257.0 MB, ratio: 1.0x)
Kernel launches: 1 (ref: 8, fusion ratio: 8.0)
Energy: 1693.48 mJ/run (ref: 0.0 mJ, ratio: -1x)
SOL score: 0.7748
final submitted kernel (82 lines, 3,071 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 <c10/cuda/CUDAGuard.h>
#include <cublas_v2.h>
#define CUBLAS_CHECK(cmd) do { \
cublasStatus_t status = (cmd); \
TORCH_CHECK(status == CUBLAS_STATUS_SUCCESS, \
"cuBLAS error, status=", (int)status); \
} while (0)
torch::Tensor matmul_at_bt(torch::Tensor A, torch::Tensor B) {
TORCH_CHECK(A.is_cuda(), "A must be a CUDA tensor");
TORCH_CHECK(B.is_cuda(), "B must be a CUDA tensor");
TORCH_CHECK(A.scalar_type() == at::kFloat, "A must be float32");
TORCH_CHECK(B.scalar_type() == at::kFloat, "B must be float32");
TORCH_CHECK(A.is_contiguous(), "A must be contiguous");
TORCH_CHECK(B.is_contiguous(), "B must be contiguous");
TORCH_CHECK(A.dim() == 2 && B.dim() == 2, "inputs must be 2D");
TORCH_CHECK(A.size(0) == B.size(1), "inner dimension mismatch");
const int64_t K64 = A.size(0);
const int64_t M64 = A.size(1);
const int64_t N64 = B.size(0);
TORCH_CHECK(K64 <= INT_MAX && M64 <= INT_MAX && N64 <= INT_MAX, "matrix too large");
const int K = static_cast<int>(K64);
const int M = static_cast<int>(M64);
const int N = static_cast<int>(N64);
c10::cuda::CUDAGuard device_guard(A.device());
auto C = torch::empty({M64, N64}, A.options());
cublasHandle_t handle = at::cuda::getCurrentCUDABlasHandle();
CUBLAS_CHECK(cublasSetStream(handle, at::cuda::getCurrentCUDAStream()));
// Match fp32 PyTorch matmul numerics (no TF32 tensor-core approximation).
CUBLAS_CHECK(cublasSetMathMode(handle, CUBLAS_DEFAULT_MATH));
const float alpha = 1.0f;
const float beta = 0.0f;
// Row-major A is KxM and row-major B is NxK. Interpreted as column-major,
// A stores an MxK matrix A_col with A_col[m,k] = A[k,m], and B stores a
// KxN matrix B_col with B_col[k,n] = B[n,k]. The row-major output C (MxN)
// is the same memory as a column-major NxM matrix C_col = C^T, so compute:
// C_col = B_col^T * A_col^T.
CUBLAS_CHECK(cublasSgemm(handle,
CUBLAS_OP_T, CUBLAS_OP_T,
N, M, K,
&alpha,
B.data_ptr<float>(), K,
A.data_ptr<float>(), M,
&beta,
C.data_ptr<float>(), N));
return C;
}
'''
_ext = load_inline(
name='matmul_at_bt_cublas_ext_v1',
cpp_sources=_cpp_src,
cuda_sources='',
functions=['matmul_at_bt'],
with_cuda=True,
extra_cflags=['-O3'],
extra_cuda_cflags=['-O3'],
extra_ldflags=['-lcublas'],
verbose=False,
)
class ModelNew(nn.Module):
def __init__(self):
super(ModelNew, self).__init__()
def forward(self, A: torch.Tensor, B: torch.Tensor) -> torch.Tensor:
return _ext.matmul_at_bt(A, B)