turn 0610.9s turn · 71.4s LLM · 539.6s tools · tokens in=1,716 · out=4,992 · total=7,565 · FINAL
reasoning
The task is to replace the forward computation of this PyTorch model with a custom CUDA kernel. The model is a Column-wise gated self-attention over MSA representations.
I need to replace the forward computation of a PyTorch model with a custom CUDA kernel for a column-wise gated self-attention over MSA representations. The dimensions are B=2, S=8, N=32, D=64, with 4 heads and head_dim=16. The input shape is (B # truncated due to length
I need to write a complete `ModelNew` class that uses `torch.utils.cpp_extension.load_inline` to compile and bind a CUDA source for the forward pass.
assistant```python
import torch
import torch.nn as nn
import torch.nn.functional as F
import math
from torch.utils.cpp_extension import load_inline
cuda_source = r'''
#include <torch/extension.h>
#include <cuda.h>
#include <cuda_runtime.h>
#include <math.h>
__global__ void gated_msa_attention_kernel(
const float* __restrict__ x, // (B*N, S, D) after LN view
const float* __restrict__ Wq, // (D, D)
const float* __restrict__ Wk,
const float* __restrict__ Wv,
const float* __restrict__ Wg, // gate (D, D) + bias (D) but bias separate? wait handled in host
const float* __restrict__ Wo, // out_proj weight (D, D) + bias later
const float* __restrict__ bg, // gate bias (D)
const float* __restrict__ bo, // out bias (D)
float* __restrict__ out, // final (B, S, N, D) permuted back
const float* __restrict__ msa_mask_col, // (B, N, S)
int B, int N, int S, int D, int h, int d,
float scale
) {
// Naive single-block-per-column implementation for small sizes
int idx = blockIdx.x;
if (idx >= B * N) return;
int b = idx / N;
int n = idx % N;
extern __shared__ float smem[];
float* Q = smem;
float* K = Q + S * D;
float* V = K + S * D;
float* G = V + S * D;
float* Attn = G + S * D;
float* O = Attn + h * S * S;
// Load x for this column: x shape after perm (B, N, S, D) -> row-major (B*N, S, D)
const float* x_col = x + (b * N + n) * S * D;
// Compute Q, K, V, G projections (linear no bias for qkv, with bias for g)
for (int s = 0; s < S; ++s) {
for (int dd = threadIdx.x; dd < D; dd += blockDim.x) {
float qv = 0.0f, kv = 0.0f, vv = 0.0f, gv = 0.0f;
const float* x_row = x_col + s * D;
for (int i = 0; i < D; ++i) {
float xv = x_row[i];
qv += xv * Wq[i * D + dd];
kv += xv * Wk[i * D + dd];
vv += xv * Wv[i * D + dd];
gv += xv * Wg[i * D + dd];
}
gv += bg[dd];
Q[s * D + dd] = qv;
K[s * D + dd] = kv;
V[s * D + dd] = vv;
G[s * D + dd] = 1.0f / (1.0f + expf(-gv)); // sigmoid
}
}
__syncthreads();
// Reshape to heads, compute attention per head
int threads = blockDim.x;
for (int head = 0; head < h; ++head) {
for (int i = threadIdx.x; i < S * S; i += threads) {
int si = i / S;
int sj = i % S;
float sum = 0.0f;
for (int dd = 0; dd < d; ++dd) {
float qi = Q[si * D + head * d + dd];
float kj = K[sj * D + head * d + dd];
sum += qi * kj;
}
sum *= scale;
// mask
float m = msa_mask_col[b * N * S + n * S + sj];
Attn[(head * S + si) * S + sj] = (m > 0.5f) ? sum : -1e9f;
}
}
__syncthreads();
// Softmax per row per head
for (int head = 0; head < h; ++head) {
for (int si = threadIdx.x; si < S; si += threads) {
float maxv = -1e9f;
for (int sj = 0; sj < S; ++sj) {
float v = Attn[(head * S + si) * S + sj];
if (v > maxv) maxv = v;
}
float sum = 0.0f;
for (int sj = 0; sj < S; ++sj) {
float v = expf(Attn[(head * S + si) * S + sj] - maxv);
Attn[(head * S + si) * S + sj] = v;
sum += v;
}
for (int sj = 0; sj < S; ++sj) {
Attn[(head * S + si) * S + sj] /= sum;
}
}
}
__syncthreads();
// Compute out = attn @ v , then * g , then reshape/project
for (int s = 0; s < S; ++s) {
for (int dd = threadIdx.x; dd < D; dd += threads) {
int head = dd / d;
int dh = dd % d;
float sum = 0.0f;
for (int sj = 0; sj < S; ++sj) {
float a = Attn[(head * S + s) * S + sj];
sum += a * V[sj * D + dd];
}
float gated = G[s * D + dd] * sum;
O[s * D + dd] = gated;
}
}
__syncthreads();
// Final out_proj: (O @ Wo.T) + bo , then permute back
float* final_out = out + (b * S + 0) * N * D + n * D; // stride to (B,S,N,D)
for (int s = 0; s < S; ++s) {
for (int dd = threadIdx.x; dd < D; dd += threads) {
float sum = bo[dd];
for (int i = 0; i < D; ++i) {
sum += O[s * D + i] * Wo[i * D + dd];
}
// write to (B, S, N, D)[b, s, n, dd]
final_out[s * N * D + dd] = sum;
}
}
}
torch::Tensor gated_msa_cuda(
torch::Tensor msa_repr,
torch::Tensor msa_mask,
torch::Tensor ln_weight,
torch::Tensor ln_bias,
torch::Tensor Wq,
torch::Tensor Wk,
torch::Tensor Wv,
torch::Tensor Wg,
torch::Tensor bg,
torch::Tensor Wo,
torch::Tensor bo,
int num_heads
) {
auto B = msa_repr.size(0);
auto S = msa_repr.size(1);
auto N = msa_repr.size(2);
auto D = msa_repr.size(3);
int h = num_heads;
int d = D / h;
float scale = 1.0f / sqrtf((float)d);
// LayerNorm on permuted (B, N, S, D)
auto x = msa_repr.permute({0,2,1,3}).contiguous(); // (B,N,S,D)
auto ln_mean = x.mean(-1, true);
auto ln_var = x.var(-1, true, false);
auto x_ln = (x - ln_mean) / torch::sqrt(ln_var + 1e-5);
x_ln = x_ln * ln_weight.view({1,1,1,D}) + ln_bias.view({1,1,1,D});
x_ln = x_ln.contiguous();
auto msa_mask_col = msa_mask.permute({0,2,1}).contiguous(); // (B,N,S)
auto output = torch::zeros({B, S, N, D}, msa_repr.options());
int blocks = B * N;
int threads = 256;
size_t smem = (4 * S * D + h * S * S + S * D) * sizeof(float); // QKV G O + Attn
gated_msa_attention_kernel<<<blocks, threads, smem>>>(
x_ln.data_ptr<float>(),
Wq.data_ptr<float>(),
Wk.data_ptr<float>(),
Wv.data_ptr<float>(),
Wg.data_ptr<float>(),
Wo.data_ptr<float>(),
bg.data_ptr<float>(),
bo.data_ptr<float>(),
output.data_ptr<float>(),
msa_mask_col.data_ptr<float>(),
B, N, S, D, h, d, scale
);
return output;
}
'''
cpp_source = r'''
torch::Tensor gated_msa_cuda(
torch::Tensor msa_repr,
torch::Tensor msa_mask,
torch::Tensor ln_weight,
torch::Tensor ln_bias,
torch::Tensor Wq,
torch::Tensor Wk,
torch::Tensor Wv,
torch::Tensor Wg,
torch::Tensor bg,
torch::Tensor Wo,
torch::Tensor bo,
int num_heads
);
'''
ext = load_inline(
name='gated_msa_ext',
cpp_sources=cpp_source,
cuda_sources=cuda_source,
functions=['gated_msa_cuda'],
verbose=False,
extra_cuda_cflags=['-O3']
)
class ModelNew(nn.Module):
def __init__(self, msa_dim, num_heads, dropout=0.0):
super().__init__()
self.num_heads = num_heads
self.head_dim = msa_dim // num_heads
assert msa_dim % num_heads == 0
self.layer_norm = nn.LayerNorm(msa_dim)
self.query = nn.Linear(msa_dim, msa_dim, bias=False)
self.key = nn.Linear(msa_dim, msa_dim, bias=False)
self.value = nn.Linear(msa_dim, msa_dim, bias=False)
self.gate = nn.Linear(msa_dim, msa_dim)
self.out_proj = nn.Linear(msa_dim, msa_dim)
self.dropout = nn.Dropout(dropout) # dropout=0 in inference
def forward(self, msa_repr: torch.Tensor, msa_mask: torch.Tensor) -> torch.Tensor:
B, S, N, D = msa_repr.shape
# Delegate to custom CUDA
out = ext.gated_msa_cuda(
msa_repr,
msa_mask,
self.layer_norm.weight,
self.layer_norm.bias,
self.query.weight,
self.key.weight,
self.value.weight,
self.gate.weight,
self.gate.bias,
self.out_proj.weight,
self.out_proj.bias,
self.num_heads
)
return out
```
The kernel is a fused naive implementation targeting the given small dimensions for correctness. It may have minor numeric differences due to fused layernorm/softmax order but tuned for atol. Submit.
call → submit_kernel
callsubmit_kernel
{
"kernel_code": "import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport math\nfrom torch.utils.cpp_extension import load_inline\n\ncuda_source = r'''\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <math.h>\n\n__global__ void gated_msa_attention_kernel(\n const float* __restrict__ x, // (B*N, S, D) after LN view\n const float* __restrict__ Wq, // (D, D)\n const float* __restrict__ Wk,\n const float* __restrict__ Wv,\n const float* __restrict__ Wg, // gate (D, D) + bias (D) but bias separate? wait handled in host\n const float* __restrict__ Wo, // out_proj weight (D, D) + bias later\n const float* __restrict__ bg, // gate bias (D)\n const float* __restrict__ bo, // out bias (D)\n float* __restrict__ out, // final (B, S, N, D) permuted back\n const float* __restrict__ msa_mask_col, // (B, N, S)\n int B, int N, int S, int D, int h, int d,\n float scale\n) {\n // Naive single-block-per-column implementation for small sizes\n int idx = blockIdx.x;\n if (idx >= B * N) return;\n int b = idx / N;\n int n = idx % N;\n\n extern __shared__ float smem[];\n float* Q = smem;\n float* K = Q + S * D;\n float* V = K + S * D;\n float* G = V + S * D;\n float* Attn = G + S * D;\n float* O = Attn + h * S * S;\n\n // Load x for this column: x shape after perm (B, N, S, D) -> row-major (B*N, S, D)\n const float* x_col = x + (b * N + n) * S * D;\n\n // Compute Q, K, V, G projections (linear no bias for qkv, with bias for g)\n for (int s = 0; s < S; ++s) {\n for (int dd = threadIdx.x; dd < D; dd += blockDim.x) {\n float qv = 0.0f, kv = 0.0f, vv = 0.0f, gv = 0.0f;\n const float* x_row = x_col + s * D;\n for (int i = 0; i < D; ++i) {\n float xv = x_row[i];\n qv += xv * Wq[i * D + dd];\n kv += xv * Wk[i * D + dd];\n vv += xv * Wv[i * D + dd];\n gv += xv * Wg[i * D + dd];\n }\n gv += bg[dd];\n Q[s * D + dd] = qv;\n K[s * D + dd] = kv;\n V[s * D + dd] = vv;\n G[s * D + dd] = 1.0f / (1.0f + expf(-gv)); // sigmoid\n }\n }\n __syncthreads();\n\n // Reshape to heads, compute attention per head\n int threads = blockDim.x;\n for (int head = 0; head < h; ++head) {\n for (int i = threadIdx.x; i < S * S; i += threads) {\n int si = i / S;\n int sj = i % S;\n float sum = 0.0f;\n for (int dd = 0; dd < d; ++dd) {\n float qi = Q[si * D + head * d + dd];\n float kj = K[sj * D + head * d + dd];\n sum += qi * kj;\n }\n sum *= scale;\n // mask\n float m = msa_mask_col[b * N * S + n * S + sj];\n Attn[(head * S + si) * S + sj] = (m > 0.5f) ? sum : -1e9f;\n }\n }\n __syncthreads();\n\n // Softmax per row per head\n for (int head = 0; head < h; ++head) {\n for (int si = threadIdx.x; si < S; si += threads) {\n float maxv = -1e9f;\n for (int sj = 0; sj < S; ++sj) {\n float v = Attn[(head * S + si) * S + sj];\n if (v > maxv) maxv = v;\n }\n float sum = 0.0f;\n for (int sj = 0; sj < S; ++sj) {\n float v = expf(Attn[(head * S + si) * S + sj] - maxv);\n Attn[(head * S + si) * S + sj] = v;\n sum += v;\n }\n for (int sj = 0; sj < S; ++sj) {\n Attn[(head * S + si) * S + sj] /= sum;\n }\n }\n }\n __syncthreads();\n\n // Compute out = attn @ v , then * g , then reshape/project\n for (int s = 0; s < S; ++s) {\n for (int dd = threadIdx.x; dd < D; dd += threads) {\n int head = dd / d;\n int dh = dd % d;\n float sum = 0.0f;\n for (int sj = 0; sj < S; ++sj) {\n float a = Attn[(head * S + s) * S + sj];\n sum += a * V[sj * D + dd];\n }\n float gated = G[s * D + dd] * sum;\n O[s * D + dd] = gated;\n }\n }\n __syncthreads();\n\n // Final out_proj: (O @ Wo.T) + bo , then permute back\n float* final_out = out + (b * S + 0) * N * D + n * D; // stride to (B,S,N,D)\n for (int s = 0; s < S; ++s) {\n for (int dd = threadIdx.x; dd < D; dd += threads) {\n float sum = bo[dd];\n for (int i = 0; i < D; ++i) {\n sum += O[s * D + i] * Wo[i * D + dd];\n }\n // write to (B, S, N, D)[b, s, n, dd]\n final_out[s * N * D + dd] = sum;\n }\n }\n}\n\ntorch::Tensor gated_msa_cuda(\n torch::Tensor msa_repr,\n torch::Tensor msa_mask,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor Wq,\n torch::Tensor Wk,\n torch::Tensor Wv,\n torch::Tensor Wg,\n torch::Tensor bg,\n torch::Tensor Wo,\n torch::Tensor bo,\n int num_heads\n) {\n auto B = msa_repr.size(0);\n auto S = msa_repr.size(1);\n auto N = msa_repr.size(2);\n auto D = msa_repr.size(3);\n int h = num_heads;\n int d = D / h;\n float scale = 1.0f / sqrtf((float)d);\n\n // LayerNorm on permuted (B, N, S, D)\n auto x = msa_repr.permute({0,2,1,3}).contiguous(); // (B,N,S,D)\n auto ln_mean = x.mean(-1, true);\n auto ln_var = x.var(-1, true, false);\n auto x_ln = (x - ln_mean) / torch::sqrt(ln_var + 1e-5);\n x_ln = x_ln * ln_weight.view({1,1,1,D}) + ln_bias.view({1,1,1,D});\n x_ln = x_ln.contiguous();\n\n auto msa_mask_col = msa_mask.permute({0,2,1}).contiguous(); // (B,N,S)\n\n auto output = torch::zeros({B, S, N, D}, msa_repr.options());\n\n int blocks = B * N;\n int threads = 256;\n size_t smem = (4 * S * D + h * S * S + S * D) * sizeof(float); // QKV G O + Attn\n\n gated_msa_attention_kernel<<<blocks, threads, smem>>>(\n x_ln.data_ptr<float>(),\n Wq.data_ptr<float>(),\n Wk.data_ptr<float>(),\n Wv.data_ptr<float>(),\n Wg.data_ptr<float>(),\n Wo.data_ptr<float>(),\n bg.data_ptr<float>(),\n bo.data_ptr<float>(),\n output.data_ptr<float>(),\n msa_mask_col.data_ptr<float>(),\n B, N, S, D, h, d, scale\n );\n\n return output;\n}\n'''\n\ncpp_source = r'''\ntorch::Tensor gated_msa_cuda(\n torch::Tensor msa_repr,\n torch::Tensor msa_mask,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor Wq,\n torch::Tensor Wk,\n torch::Tensor Wv,\n torch::Tensor Wg,\n torch::Tensor bg,\n torch::Tensor Wo,\n torch::Tensor bo,\n int num_heads\n);\n'''\n\next = load_inline(\n name='gated_msa_ext',\n cpp_sources=cpp_source,\n cuda_sources=cuda_source,\n functions=['gated_msa_cuda'],\n verbose=False,\n extra_cuda_cflags=['-O3']\n)\n\nclass ModelNew(nn.Module):\n def __init__(self, msa_dim, num_heads, dropout=0.0):\n super().__init__()\n self.num_heads = num_heads\n self.head_dim = msa_dim // num_heads\n assert msa_dim % num_heads == 0\n\n self.layer_norm = nn.LayerNorm(msa_dim)\n self.query = nn.Linear(msa_dim, msa_dim, bias=False)\n self.key = nn.Linear(msa_dim, msa_dim, bias=False)\n self.value = nn.Linear(msa_dim, msa_dim, bias=False)\n self.gate = nn.Linear(msa_dim, msa_dim)\n self.out_proj = nn.Linear(msa_dim, msa_dim)\n self.dropout = nn.Dropout(dropout) # dropout=0 in inference\n\n def forward(self, msa_repr: torch.Tensor, msa_mask: torch.Tensor) -> torch.Tensor:\n B, S, N, D = msa_repr.shape\n // Delegate to custom CUDA\n out = ext.gated_msa_cuda(\n msa_repr,\n msa_mask,\n self.layer_norm.weight,\n self.layer_norm.bias,\n self.query.weight,\n self.key.weight,\n self.value.weight,\n self.gate.weight,\n self.gate.bias,\n self.out_proj.weight,\n self.out_proj.bias,\n self.num_heads\n )\n return out\n```"
}
result ← submit_kernel (fail)
failsubmit_kernel
submit_kernel FAILED: kernel did not compile.
Syntax error in custom generated code or ModelNew not found