correct
1_DeepSeekMLALoRAExpansion.py
started
2026-05-11T13:09:16Z
timingkernelbench.timing
kernel mean / median / std
71.80 / — / 336.00 μs
ref mean / median / std
0.04 / — / 0.00 μs
numerical precisioneval_kernel_against_ref
gpu memoryextended_metrics.measure_memory
kernel launches / fusiontorch.profiler
fusion ratio (ref / kernel)
1.08x
energyNVML (pynvml)
ratio (ref / kernel)
0.04x
SOL (speed-of-light)nsight_hardware_counters
roofline / occupancynsight_hardware_counters
dram bandwidth
1878.68 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):
"""
Low-rank expansion of compressed KV activations into multi-head K/V tensors.
This isolates the MLA-style compressed-cache pattern: a hidden activation is
projected into a low-rank latent space and then expanded back into separate
key and value tensors.
"""
def __init__(self):
super().__init__()
def forward(
self,
hidden: torch.Tensor,
w_down: torch.Tensor,
w_up_k: torch.Tensor,
w_up_v: torch.Tensor,
) -> torch.Tensor:
batch_size, seq_len, model_dim = hidden.shape
rank = w_down.shape[1]
kv_heads = 4
head_dim = w_up_k.shape[1] // kv_heads
latent = hidden.reshape(batch_size * seq_len, model_dim) @ w_down
k = latent @ w_up_k
v = latent @ w_up_v
k = k.view(batch_size, seq_len, kv_heads, head_dim)
v = v.view(batch_size, seq_len, kv_heads, head_dim)
return torch.stack((k, v), dim=2)
batch_size = 4
seq_len = 96
model_dim = 256
rank = 48
kv_heads = 4
head_dim = 32
def get_inputs():
p = popcorn_pri
hidden = torch.randn(p.jitter_int(batch_size), p.jitter_int(seq_len), p.jitter_int(model_dim, align=8), dtype=torch.float32)
w_down = torch.randn(p.jitter_int(model_dim, align=8), rank, dtype=torch.float32)
w_up_k = torch.randn(rank, p.jitter_int(kv_heads) * p.jitter_int(head_dim, align=8), dtype=torch.float32)
w_up_v = torch.randn(rank, p.jitter_int(kv_heads) * p.jitter_int(head_dim, align=8), dtype=torch.float32)
return [hidden, w_down, w_up_k, w_up_v]
def get_init_inputs():
return []
turn 0213.4s turn · 5.8s LLM · 207.5s tools · tokens in=1,623 · out=6 · total=1,905
reasoning
The task is to replace the forward computation with a custom CUDA kernel that's faster than the PyTorch reference while being numerically equivalent.
call → get_gpu_specs
callget_gpu_specs
{}
result ← get_gpu_specs (ok)
okget_gpu_specs
GPU specs for NVIDIA H100 80GB HBM3:
total memory (runtime): 79.1 GB
spec entry: H100
GPU Architecture: Hopper
GPU Memory: 80GB
Memory Bandwidth: 3.35 TB/s
FP64 TFLOPS: 34
FP64 Tensor Core TFLOPS: 67
FP32 TFLOPS: 67
TF32 Tensor Core TFLOPS: 989 with sparsity
BFLOAT16 Tensore Core TFLOPS: 1979 with sparsity
FP16 Tensor Core TFLOPS: 1979 with sparsity
FP8 Tensor Core TFLOPS: 3958 with sparsity
INT8 Tensor Core TOPS: 3958 with sparsity
Register File Size: 64K 32-bit registers per SM
Maximum number of registers per thread: 255
Maximum number of thread blocks per SM: 32
Shared memory capacity per SM: 228 KB
Maximum shared memory per thread block: 227 KB
turn 1363.5s turn · 13.5s LLM · 350.0s tools · tokens in=2,138 · out=887 · total=3,355
reasoning
It's an H100, high bandwidth, tensor cores, but since we need fp32 and custom CUDA, probably no tensor cores easily unless using wmma or something, but for fp32, standard GEMM with FP32.
call → compile_kernel
callcompile_kernel
{
"kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\ncuda_source = \"\"\"\n#include <torch/extension.h>\n#include <cuda_runtime.h>\n\n__global__ void mla_kernel(\n const float* __restrict__ hidden,\n const float* __restrict__ w_down,\n const float* __restrict__ w_up_k,\n const float* __restrict__ w_up_v,\n float* __restrict__ output,\n int batch_size, int seq_len, int model_dim, int rank, int kv_heads, int head_dim\n) {\n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n int total = batch_size * seq_len * 2 * kv_heads * head_dim;\n if (idx >= total) return;\n\n // decode indices\n int h_dim = head_dim;\n int kvh = kv_heads;\n int hd = h_dim;\n int out_per_token = 2 * kvh * hd;\n int token_idx = idx / out_per_token;\n int rem = idx % out_per_token;\n int kv = rem / (kvh * hd);\n rem = rem % (kvh * hd);\n int h = rem / hd;\n int d = rem % hd;\n\n int b = token_idx / seq_len;\n int s = token_idx % seq_len;\n\n // compute latent\n float lat[48]; // assume rank <=48\n for(int r=0; r<rank; r++) {\n float sum = 0.0f;\n for(int m=0; m<model_dim; m++) {\n sum += hidden[token_idx * model_dim + m] * w_down[m * rank + r];\n }\n lat[r] = sum;\n }\n\n // now compute k or v\n const float* w_up = (kv == 0) ? w_up_k : w_up_v;\n float val = 0.0f;\n int out_dim = kvh * hd;\n int col = h * hd + d;\n for(int r=0; r<rank; r++) {\n val += lat[r] * w_up[r * out_dim + col];\n }\n output[idx] = val;\n}\n\ntorch::Tensor mla_forward(\n torch::Tensor hidden,\n torch::Tensor w_down,\n torch::Tensor w_up_k,\n torch::Tensor w_up_v\n) {\n int batch_size = hidden.size(0);\n int seq_len = hidden.size(1);\n int model_dim = hidden.size(2);\n int rank = w_down.size(1);\n int kv_heads = 4;\n int head_dim = w_up_k.size(1) / kv_heads;\n\n auto options = torch::TensorOptions().dtype(torch::kFloat32).device(hidden.device());\n auto output = torch::empty({batch_size, seq_len, 2, kv_heads, head_dim}, options);\n\n int total = batch_size * seq_len * 2 * kv_heads * head_dim;\n int threads = 256;\n int blocks = (total + threads - 1) / threads;\n\n mla_kernel<<<blocks, threads>>>(\n hidden.data_ptr<float>(),\n w_down.data_ptr<float>(),\n w_up_k.data_ptr<float>(),\n w_up_v.data_ptr<float>(),\n output.data_ptr<float>(),\n batch_size, seq_len, model_dim, rank, kv_heads, head_dim\n );\n\n return output;\n}\n\"\"\"\n\ncpp_source = \"\"\"\ntorch::Tensor mla_forward(torch::Tensor hidden, torch::Tensor w_down, torch::Tensor w_up_k, torch::Tensor w_up_v);\n\"\"\"\n\nmodule = load_inline(\n name='mla_cuda',\n cpp_sources=[cpp_source],\n cuda_sources=[cuda_source],\n functions=['mla_forward'],\n verbose=True,\n extra_cuda_cflags=['-O3']\n)\n\nclass ModelNew(nn.Module):\n def __init__(self):\n super().__init__()\n self.mla = module\n\n def forward(self, hidden, w_down, w_up_k, w_up_v):\n return self.mla.mla_forward(hidden, w_down, w_up_k, w_up_v)\n"
}
result ← compile_kernel (ok)
okcompile_kernel
compile_kernel PASSED: kernel compiled without errors.
turn 2322.8s turn · 6.1s LLM · 316.7s tools · tokens in=3,372 · out=888 · total=4,267
reasoning
Compiled ok. Now check correctness.
call → run_correctness
callrun_correctness
{
"kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\ncuda_source = \"\"\"\n#include <torch/extension.h>\n#include <cuda_runtime.h>\n\n__global__ void mla_kernel(\n const float* __restrict__ hidden,\n const float* __restrict__ w_down,\n const float* __restrict__ w_up_k,\n const float* __restrict__ w_up_v,\n float* __restrict__ output,\n int batch_size, int seq_len, int model_dim, int rank, int kv_heads, int head_dim\n) {\n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n int total = batch_size * seq_len * 2 * kv_heads * head_dim;\n if (idx >= total) return;\n\n // decode indices\n int h_dim = head_dim;\n int kvh = kv_heads;\n int hd = h_dim;\n int out_per_token = 2 * kvh * hd;\n int token_idx = idx / out_per_token;\n int rem = idx % out_per_token;\n int kv = rem / (kvh * hd);\n rem = rem % (kvh * hd);\n int h = rem / hd;\n int d = rem % hd;\n\n int b = token_idx / seq_len;\n int s = token_idx % seq_len;\n\n // compute latent\n float lat[48]; // assume rank <=48\n for(int r=0; r<rank; r++) {\n float sum = 0.0f;\n for(int m=0; m<model_dim; m++) {\n sum += hidden[token_idx * model_dim + m] * w_down[m * rank + r];\n }\n lat[r] = sum;\n }\n\n // now compute k or v\n const float* w_up = (kv == 0) ? w_up_k : w_up_v;\n float val = 0.0f;\n int out_dim = kvh * hd;\n int col = h * hd + d;\n for(int r=0; r<rank; r++) {\n val += lat[r] * w_up[r * out_dim + col];\n }\n output[idx] = val;\n}\n\ntorch::Tensor mla_forward(\n torch::Tensor hidden,\n torch::Tensor w_down,\n torch::Tensor w_up_k,\n torch::Tensor w_up_v\n) {\n int batch_size = hidden.size(0);\n int seq_len = hidden.size(1);\n int model_dim = hidden.size(2);\n int rank = w_down.size(1);\n int kv_heads = 4;\n int head_dim = w_up_k.size(1) / kv_heads;\n\n auto options = torch::TensorOptions().dtype(torch::kFloat32).device(hidden.device());\n auto output = torch::empty({batch_size, seq_len, 2, kv_heads, head_dim}, options);\n\n int total = batch_size * seq_len * 2 * kv_heads * head_dim;\n int threads = 256;\n int blocks = (total + threads - 1) / threads;\n\n mla_kernel<<<blocks, threads>>>(\n hidden.data_ptr<float>(),\n w_down.data_ptr<float>(),\n w_up_k.data_ptr<float>(),\n w_up_v.data_ptr<float>(),\n output.data_ptr<float>(),\n batch_size, seq_len, model_dim, rank, kv_heads, head_dim\n );\n\n return output;\n}\n\"\"\"\n\ncpp_source = \"\"\"\ntorch::Tensor mla_forward(torch::Tensor hidden, torch::Tensor w_down, torch::Tensor w_up_k, torch::Tensor w_up_v);\n\"\"\"\n\nmodule = load_inline(\n name='mla_cuda',\n cpp_sources=[cpp_source],\n cuda_sources=[cuda_source],\n functions=['mla_forward'],\n verbose=False,\n extra_cuda_cflags=['-O3']\n)\n\nclass ModelNew(nn.Module):\n def __init__(self):\n super().__init__()\n self.mla = module\n\n def forward(self, hidden, w_down, w_up_k, w_up_v):\n return self.mla.mla_forward(hidden, w_down, w_up_k, w_up_v)\n"
}
result ← run_correctness (fail)
failrun_correctness
run_correctness FAILED: (3 / 5) trials did not all match.
correctness_issue: Output mismatch
max_difference: ['0.000214', '0.000214']
avg_difference: ['0.000030', '0.000031']
turn 3188.2s turn · 13.6s LLM · 174.6s tools · tokens in=4,328 · out=769 · total=5,275
reasoning
Almost, max diff 2e-4, tolerance is 1e-4, close but fails.
call → compile_kernel
callcompile_kernel
{
"kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\ncuda_source = \"\"\"\n#include <torch/extension.h>\n#include <cuda_runtime.h>\n#include <cublas_v2.h>\n\ntorch::Tensor mla_forward(\n torch::Tensor hidden,\n torch::Tensor w_down,\n torch::Tensor w_up_k,\n torch::Tensor w_up_v\n) {\n int batch_size = hidden.size(0);\n int seq_len = hidden.size(1);\n int model_dim = hidden.size(2);\n int rank = w_down.size(1);\n int kv_heads = 4;\n int head_dim = w_up_k.size(1) / kv_heads;\n int bs = batch_size * seq_len;\n int kv_dim = kv_heads * head_dim;\n\n auto options = torch::TensorOptions().dtype(torch::kFloat32).device(hidden.device());\n auto latent = torch::empty({bs, rank}, options);\n auto k = torch::empty({bs, kv_dim}, options);\n auto v = torch::empty({bs, kv_dim}, options);\n auto output = torch::empty({batch_size, seq_len, 2, kv_heads, head_dim}, options);\n\n cublasHandle_t handle;\n cublasCreate(&handle);\n float alpha = 1.0f, beta = 0.0f;\n\n // hidden @ w_down -> latent : (bs x model) @ (model x rank)\n cublasSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N,\n rank, bs, model_dim,\n &alpha,\n w_down.data_ptr<float>(), rank,\n hidden.data_ptr<float>(), model_dim,\n &beta,\n latent.data_ptr<float>(), rank);\n\n // latent @ w_up_k -> k\n cublasSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N,\n kv_dim, bs, rank,\n &alpha,\n w_up_k.data_ptr<float>(), kv_dim,\n latent.data_ptr<float>(), rank,\n &beta,\n k.data_ptr<float>(), kv_dim);\n\n // latent @ w_up_v -> v\n cublasSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N,\n kv_dim, bs, rank,\n &alpha,\n w_up_v.data_ptr<float>(), kv_dim,\n latent.data_ptr<float>(), rank,\n &beta,\n v.data_ptr<float>(), kv_dim);\n\n // reshape and stack\n auto k_view = k.view({batch_size, seq_len, kv_heads, head_dim});\n auto v_view = v.view({batch_size, seq_len, kv_heads, head_dim});\n output.select(2, 0).copy_(k_view);\n output.select(2, 1).copy_(v_view);\n\n cublasDestroy(handle);\n return output;\n}\n\"\"\"\n\ncpp_source = \"\"\"\ntorch::Tensor mla_forward(torch::Tensor hidden, torch::Tensor w_down, torch::Tensor w_up_k, torch::Tensor w_up_v);\n\"\"\"\n\nmodule = load_inline(\n name='mla_cublas',\n cpp_sources=[cpp_source],\n cuda_sources=[cuda_source],\n functions=['mla_forward'],\n verbose=True,\n extra_cuda_cflags=['-O3'],\n extra_ldflags=['-lcublas']\n)\n\nclass ModelNew(nn.Module):\n def __init__(self):\n super().__init__()\n self.mla = module\n\n def forward(self, hidden, w_down, w_up_k, w_up_v):\n return self.mla.mla_forward(hidden, w_down, w_up_k, w_up_v)\n"
}
result ← compile_kernel (ok)
okcompile_kernel
compile_kernel PASSED: kernel compiled without errors.
turn 4590.3s turn · 8.4s LLM · 581.9s tools · tokens in=5,292 · out=770 · total=6,066
reasoning
Now test correctness.
call → run_correctness
callrun_correctness
{
"kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\ncuda_source = \"\"\"\n#include <torch/extension.h>\n#include <cuda_runtime.h>\n#include <cublas_v2.h>\n\ntorch::Tensor mla_forward(\n torch::Tensor hidden,\n torch::Tensor w_down,\n torch::Tensor w_up_k,\n torch::Tensor w_up_v\n) {\n int batch_size = hidden.size(0);\n int seq_len = hidden.size(1);\n int model_dim = hidden.size(2);\n int rank = w_down.size(1);\n int kv_heads = 4;\n int head_dim = w_up_k.size(1) / kv_heads;\n int bs = batch_size * seq_len;\n int kv_dim = kv_heads * head_dim;\n\n auto options = torch::TensorOptions().dtype(torch::kFloat32).device(hidden.device());\n auto latent = torch::empty({bs, rank}, options);\n auto k = torch::empty({bs, kv_dim}, options);\n auto v = torch::empty({bs, kv_dim}, options);\n auto output = torch::empty({batch_size, seq_len, 2, kv_heads, head_dim}, options);\n\n cublasHandle_t handle;\n cublasCreate(&handle);\n float alpha = 1.0f, beta = 0.0f;\n\n // hidden @ w_down -> latent : (bs x model) @ (model x rank)\n cublasSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N,\n rank, bs, model_dim,\n &alpha,\n w_down.data_ptr<float>(), rank,\n hidden.data_ptr<float>(), model_dim,\n &beta,\n latent.data_ptr<float>(), rank);\n\n // latent @ w_up_k -> k\n cublasSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N,\n kv_dim, bs, rank,\n &alpha,\n w_up_k.data_ptr<float>(), kv_dim,\n latent.data_ptr<float>(), rank,\n &beta,\n k.data_ptr<float>(), kv_dim);\n\n // latent @ w_up_v -> v\n cublasSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N,\n kv_dim, bs, rank,\n &alpha,\n w_up_v.data_ptr<float>(), kv_dim,\n latent.data_ptr<float>(), rank,\n &beta,\n v.data_ptr<float>(), kv_dim);\n\n // reshape and stack\n auto k_view = k.view({batch_size, seq_len, kv_heads, head_dim});\n auto v_view = v.view({batch_size, seq_len, kv_heads, head_dim});\n output.select(2, 0).copy_(k_view);\n output.select(2, 1).copy_(v_view);\n\n cublasDestroy(handle);\n return output;\n}\n\"\"\"\n\ncpp_source = \"\"\"\ntorch::Tensor mla_forward(torch::Tensor hidden, torch::Tensor w_down, torch::Tensor w_up_k, torch::Tensor w_up_v);\n\"\"\"\n\nmodule = load_inline(\n name='mla_cublas',\n cpp_sources=[cpp_source],\n cuda_sources=[cuda_source],\n functions=['mla_forward'],\n verbose=False,\n extra_cuda_cflags=['-O3'],\n extra_ldflags=['-lcublas']\n)\n\nclass ModelNew(nn.Module):\n def __init__(self):\n super().__init__()\n self.mla = module\n\n def forward(self, hidden, w_down, w_up_k, w_up_v):\n return self.mla.mla_forward(hidden, w_down, w_up_k, w_up_v)\n"
}
result ← run_correctness (ok)
okrun_correctness
run_correctness PASSED: (5 / 5) trials all matched the reference.
Numerical precision: max_abs_err=0.00e+00 mean_abs_err=0.00e+00 max_rel_err=0.00e+00
turn 5515.8s turn · 9.2s LLM · 506.7s tools · tokens in=6,126 · out=769 · total=6,918 · FINAL
reasoning
Perfect, matches exactly. Now, since cublas is highly optimized, it should be fast.
call → submit_kernel
callsubmit_kernel
{
"kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\ncuda_source = \"\"\"\n#include <torch/extension.h>\n#include <cuda_runtime.h>\n#include <cublas_v2.h>\n\ntorch::Tensor mla_forward(\n torch::Tensor hidden,\n torch::Tensor w_down,\n torch::Tensor w_up_k,\n torch::Tensor w_up_v\n) {\n int batch_size = hidden.size(0);\n int seq_len = hidden.size(1);\n int model_dim = hidden.size(2);\n int rank = w_down.size(1);\n int kv_heads = 4;\n int head_dim = w_up_k.size(1) / kv_heads;\n int bs = batch_size * seq_len;\n int kv_dim = kv_heads * head_dim;\n\n auto options = torch::TensorOptions().dtype(torch::kFloat32).device(hidden.device());\n auto latent = torch::empty({bs, rank}, options);\n auto k = torch::empty({bs, kv_dim}, options);\n auto v = torch::empty({bs, kv_dim}, options);\n auto output = torch::empty({batch_size, seq_len, 2, kv_heads, head_dim}, options);\n\n cublasHandle_t handle;\n cublasCreate(&handle);\n float alpha = 1.0f, beta = 0.0f;\n\n // hidden @ w_down -> latent : (bs x model) @ (model x rank)\n cublasSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N,\n rank, bs, model_dim,\n &alpha,\n w_down.data_ptr<float>(), rank,\n hidden.data_ptr<float>(), model_dim,\n &beta,\n latent.data_ptr<float>(), rank);\n\n // latent @ w_up_k -> k\n cublasSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N,\n kv_dim, bs, rank,\n &alpha,\n w_up_k.data_ptr<float>(), kv_dim,\n latent.data_ptr<float>(), rank,\n &beta,\n k.data_ptr<float>(), kv_dim);\n\n // latent @ w_up_v -> v\n cublasSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N,\n kv_dim, bs, rank,\n &alpha,\n w_up_v.data_ptr<float>(), kv_dim,\n latent.data_ptr<float>(), rank,\n &beta,\n v.data_ptr<float>(), kv_dim);\n\n // reshape and stack\n auto k_view = k.view({batch_size, seq_len, kv_heads, head_dim});\n auto v_view = v.view({batch_size, seq_len, kv_heads, head_dim});\n output.select(2, 0).copy_(k_view);\n output.select(2, 1).copy_(v_view);\n\n cublasDestroy(handle);\n return output;\n}\n\"\"\"\n\ncpp_source = \"\"\"\ntorch::Tensor mla_forward(torch::Tensor hidden, torch::Tensor w_down, torch::Tensor w_up_k, torch::Tensor w_up_v);\n\"\"\"\n\nmodule = load_inline(\n name='mla_cublas',\n cpp_sources=[cpp_source],\n cuda_sources=[cuda_source],\n functions=['mla_forward'],\n verbose=False,\n extra_cuda_cflags=['-O3'],\n extra_ldflags=['-lcublas']\n)\n\nclass ModelNew(nn.Module):\n def __init__(self):\n super().__init__()\n self.mla = module\n\n def forward(self, hidden, w_down, w_up_k, w_up_v):\n return self.mla.mla_forward(hidden, w_down, w_up_k, w_up_v)\n"
}
result ← submit_kernel (ok)
oksubmit_kernel
submit_kernel PASSED: (5 / 5) correctness trials all passed.
Kernel runtime: 71.80 μs
Runtime stats: mean=71.80μs median=?μs std=336.00μs
Numerical precision: max_abs_err=0.00e+00 mean_abs_err=0.00e+00 max_rel_err=0.00e+00
Memory: 34.29 MB peak (ref: 34.29 MB, ratio: 1.0x)
Kernel launches: 12 (ref: 13, fusion ratio: 1.0833)
Energy: 6073.24 mJ/run (ref: 242.76 mJ, ratio: 0.04x)
SOL score: 0.5608
final submitted kernel (94 lines, 2,860 chars) — full source, untruncated
import torch
import torch.nn as nn
from torch.utils.cpp_extension import load_inline
cuda_source = """
#include <torch/extension.h>
#include <cuda_runtime.h>
#include <cublas_v2.h>
torch::Tensor mla_forward(
torch::Tensor hidden,
torch::Tensor w_down,
torch::Tensor w_up_k,
torch::Tensor w_up_v
) {
int batch_size = hidden.size(0);
int seq_len = hidden.size(1);
int model_dim = hidden.size(2);
int rank = w_down.size(1);
int kv_heads = 4;
int head_dim = w_up_k.size(1) / kv_heads;
int bs = batch_size * seq_len;
int kv_dim = kv_heads * head_dim;
auto options = torch::TensorOptions().dtype(torch::kFloat32).device(hidden.device());
auto latent = torch::empty({bs, rank}, options);
auto k = torch::empty({bs, kv_dim}, options);
auto v = torch::empty({bs, kv_dim}, options);
auto output = torch::empty({batch_size, seq_len, 2, kv_heads, head_dim}, options);
cublasHandle_t handle;
cublasCreate(&handle);
float alpha = 1.0f, beta = 0.0f;
// hidden @ w_down -> latent : (bs x model) @ (model x rank)
cublasSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N,
rank, bs, model_dim,
&alpha,
w_down.data_ptr<float>(), rank,
hidden.data_ptr<float>(), model_dim,
&beta,
latent.data_ptr<float>(), rank);
// latent @ w_up_k -> k
cublasSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N,
kv_dim, bs, rank,
&alpha,
w_up_k.data_ptr<float>(), kv_dim,
latent.data_ptr<float>(), rank,
&beta,
k.data_ptr<float>(), kv_dim);
// latent @ w_up_v -> v
cublasSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N,
kv_dim, bs, rank,
&alpha,
w_up_v.data_ptr<float>(), kv_dim,
latent.data_ptr<float>(), rank,
&beta,
v.data_ptr<float>(), kv_dim);
// reshape and stack
auto k_view = k.view({batch_size, seq_len, kv_heads, head_dim});
auto v_view = v.view({batch_size, seq_len, kv_heads, head_dim});
output.select(2, 0).copy_(k_view);
output.select(2, 1).copy_(v_view);
cublasDestroy(handle);
return output;
}
"""
cpp_source = """
torch::Tensor mla_forward(torch::Tensor hidden, torch::Tensor w_down, torch::Tensor w_up_k, torch::Tensor w_up_v);
"""
module = load_inline(
name='mla_cublas',
cpp_sources=[cpp_source],
cuda_sources=[cuda_source],
functions=['mla_forward'],
verbose=False,
extra_cuda_cflags=['-O3'],
extra_ldflags=['-lcublas']
)
class ModelNew(nn.Module):
def __init__(self):
super().__init__()
self.mla = module
def forward(self, hidden, w_down, w_up_k, w_up_v):
return self.mla.mla_forward(hidden, w_down, w_up_k, w_up_v)