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\n# Define CUDA source for fused operations\ncuda_source = \"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <c10/cuda/CUDAGuard.h>\n#include <ATen/cuda/CUDAContext.h>\n\ntemplate <typename scalar_t>\n__global__ void layer_norm_kernel(\n const scalar_t* __restrict__ input,\n scalar_t* __restrict__ output,\n const scalar_t* __restrict__ weight,\n const scalar_t* __restrict__ bias,\n int N, int D, float eps) {\n \n int idx = blockIdx.x;\n int tid = threadIdx.x;\n \n if (idx >= N) return;\n \n __shared__ float mean;\n __shared__ float var;\n __shared__ float inv_std;\n \n float sum = 0.0f;\n float sum_sq = 0.0f;\n \n for (int i = tid; i < D; i += blockDim.x) {\n float val = (float)input[idx * D + i];\n sum += val;\n sum_sq += val * val;\n }\n \n // Block reduction for sum\n for (int offset = blockDim.x / 2; offset > 0; offset >>= 1) {\n sum += __shfl_down_sync(0xffffffff, sum, offset);\n sum_sq += __shfl_down_sync(0xffffffff, sum_sq, offset);\n }\n \n if (tid == 0) {\n mean = sum / D;\n var = sum_sq / D - mean * mean;\n inv_std = 1.0f / sqrtf(var + eps);\n }\n __syncthreads();\n \n for (int i = tid; i < D; i += blockDim.x) {\n float val = (float)input[idx * D + i];\n val = (val - mean) * inv_std;\n if (weight != nullptr) val *= (float)weight[i];\n if (bias != nullptr) val += (float)bias[i];\n output[idx * D + i] = (scalar_t)val;\n }\n}\n\ntemplate <typename scalar_t>\n__global__ void fused_gate_out_kernel(\n const scalar_t* __restrict__ gate_proj,\n const scalar_t* __restrict__ attn_out,\n const scalar_t* __restrict__ out_weight,\n const scalar_t* __restrict__ out_bias,\n scalar_t* __restrict__ output,\n int B, int N, int S, int D, int h, int d) {\n \n // Each block handles one (b, n, s) position\n int b = blockIdx.x / (N * S);\n int ns = blockIdx.x % (N * S);\n int n = ns / S;\n int s = ns % S;\n int tid = threadIdx.x;\n \n extern __shared__ float shared_mem[];\n float* local_out = shared_mem; // Size D\n float* local_gate = &shared_mem[D]; // Size D\n \n // Initialize output\n for (int i = tid; i < D; i += blockDim.x) {\n local_out[i] = 0.0f;\n local_gate[i] = 0.0f;\n }\n __syncthreads();\n \n // Aggregate attention output across heads and compute sigmoid gate\n for (int hd = tid; hd < h * d; hd += blockDim.x) {\n int head = hd / d;\n int dim = hd % d;\n \n // attn_out shape: (B, N, h, S, d) -> index: b*N*h*S*d + n*h*S*d + head*S*d + s*d + dim\n int attn_idx = ((b * N + n) * h + head) * S * d + s * d + dim;\n float val = (float)attn_out[attn_idx];\n \n // gate_proj shape: (B, N, S, D) before sigmoid\n int gate_idx = (b * N + n) * S * D + s * D + head * d + dim;\n float gate_val = (float)gate_proj[gate_idx];\n \n atomicAdd(&local_gate[head * d + dim], gate_val);\n atomicAdd(&local_out[head * d + dim], val);\n }\n __syncthreads();\n \n // Apply sigmoid to gate and multiply\n for (int i = tid; i < D; i += blockDim.x) {\n float gate = 1.0f / (1.0f + expf(-local_gate[i]));\n local_out[i] = gate * local_out[i];\n }\n __syncthreads();\n \n // Output projection: local_out (D) @ out_weight (D, D) + bias\n for (int i = tid; i < D; i += blockDim.x) {\n float sum = (out_bias != nullptr) ? (float)out_bias[i] : 0.0f;\n for (int j = 0; j < D; j++) {\n sum += local_out[j] * (float)out_weight[j * D + i];\n }\n // Write to output: (B, S, N, D)\n // Input was (B, N, S, D), need to permute back\n int out_idx = b * S * N * D + s * N * D + n * D + i;\n output[out_idx] = (scalar_t)sum;\n }\n}\n\ntemplate <typename scalar_t>\n__global__ void prepare_qkv_kernel(\n const scalar_t* __restrict__ ln_out,\n const scalar_t* __restrict__ q_weight,\n const scalar_t* __restrict__ k_weight,\n const scalar_t* __restrict__ v_weight,\n const scalar_t* __restrict__ g_weight,\n const scalar_t* __restrict__ g_bias,\n scalar_t* __restrict__ q_out,\n scalar_t* __restrict__ k_out,\n scalar_t* __restrict__ v_out,\n scalar_t* __restrict__ g_out,\n int B, int N, int S, int D, int h, int d) {\n \n // Each block handles one (b, n, s) position\n int idx = blockIdx.x;\n int tid = threadIdx.x;\n \n if (idx >= B * N * S) return;\n \n int b = idx / (N * S);\n int ns = idx % (N * S);\n int n = ns / S;\n int s = ns % S;\n \n extern __shared__ float shared_mem[];\n float* ln_local = shared_mem; // Size D\n \n // Load layer norm output\n for (int i = tid; i < D; i += blockDim.x) {\n ln_local[i] = (float)ln_out[idx * D + i];\n }\n __syncthreads();\n \n // Compute Q, K, V, G projections and write in (B, N, h, S, d) format\n for (int hd = tid; hd < h * d; hd += blockDim.x) {\n int head = hd / d;\n int dim = hd % d;\n \n float q_val = 0.0f, k_val = 0.0f, v_val = 0.0f, g_val = (g_bias != nullptr) ? (float)g_bias[head * d + dim] : 0.0f;\n \n for (int j = 0; j < D; j++) {\n float ln_val = ln_local[j];\n q_val += ln_val * (float)q_weight[j * h * d + head * d + dim];\n k_val += ln_val * (float)k_weight[j * h * d + head * d + dim];\n v_val += ln_val * (float)v_weight[j * h * d + head * d + dim];\n g_val += ln_val * (float)g_weight[j * h * d + head * d + dim];\n }\n \n // Write Q, K, V in (B, N, h, S, d) format\n int out_idx = ((b * N + n) * h + head) * S * d + s * d + dim;\n q_out[out_idx] = (scalar_t)q_val;\n k_out[out_idx] = (scalar_t)k_val;\n v_out[out_idx] = (scalar_t)v_val;\n g_out[out_idx] = (scalar_t)g_val;\n }\n}\n\ntemplate <typename scalar_t>\n__global__ void softmax_mask_kernel(\n scalar_t* __restrict__ attn,\n const int* __restrict__ mask,\n int B, int N, int h, int S) {\n \n // Each block handles one (b, n, head, i) row\n int idx = blockIdx.x;\n int tid = threadIdx.x;\n \n if (idx >= B * N * h * S) return;\n \n int b = idx / (N * h * S);\n int remainder = idx % (N * h * S);\n int n = remainder / (h * S);\n remainder = remainder % (h * S);\n int head = remainder / S;\n int i = remainder % S;\n \n // Find mask index: (B, N, S) -> mask[b, n, j] for all j\n int mask_base = b * N * S + n * S;\n \n __shared__ float max_val;\n __shared__ float sum_exp;\n \n float local_max = -1e30f;\n \n // Apply mask and find max\n for (int j = tid; j < S; j += blockDim.x) {\n int attn_idx = ((b * N + n) * h + head) * S * S + i * S + j;\n if (mask[mask_base + j] == 0) {\n attn[attn_idx] = (scalar_t)(-1e30f);\n }\n float val = (float)attn[attn_idx];\n local_max = fmaxf(local_max, val);\n }\n \n // Block reduction for max\n for (int offset = blockDim.x / 2; offset > 0; offset >>= 1) {\n local_max = fmaxf(local_max, __shfl_down_sync(0xffffffff, local_max, offset));\n }\n \n if (tid == 0) {\n max_val = local_max;\n }\n __syncthreads();\n \n // Compute exp and sum\n float local_sum = 0.0f;\n for (int j = tid; j < S; j += blockDim.x) {\n int attn_idx = ((b * N + n) * h + head) * S * S + i * S + j;\n float val = expf((float)attn[attn_idx] - max_val);\n attn[attn_idx] = (scalar_t)val;\n local_sum += val;\n }\n \n // Block reduction for sum\n for (int offset = blockDim.x / 2; offset > 0; offset >>= 1) {\n local_sum += __shfl_down_sync(0xffffffff, local_sum, offset);\n }\n \n if (tid == 0) {\n sum_exp = local_sum;\n }\n __syncthreads();\n \n // Normalize\n float inv_sum = 1.0f / sum_exp;\n for (int j = tid; j < S; j += blockDim.x) {\n int attn_idx = ((b * N + n) * h + head) * S * S + i * S + j;\n attn[attn_idx] = (scalar_t)((float)attn[attn_idx] * inv_sum);\n }\n}\n\nstd::vector<torch::Tensor> msa_attention_cuda(\n torch::Tensor msa_repr,\n torch::Tensor msa_mask,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias,\n torch::Tensor q_weight,\n torch::Tensor k_weight,\n torch::Tensor v_weight,\n torch::Tensor g_weight,\n torch::Tensor g_bias,\n torch::Tensor out_weight,\n torch::Tensor out_bias,\n int num_heads,\n float scale) {\n \n auto stream = at::cuda::getCurrentCUDAStream();\n \n int B = msa_repr.size(0);\n int S = msa_repr.size(1);\n int N = msa_repr.size(2);\n int D = msa_repr.size(3);\n int h = num_heads;\n int d = D / h;\n \n // Permute input: (B, S, N, D) -> (B, N, S, D)\n auto msa_permuted = msa_repr.permute({0, 2, 1, 3}).contiguous();\n \n // Layer norm output\n auto ln_out = torch::empty_like(msa_permuted);\n \n // Apply layer norm\n int ln_threads = std::min(D, 256);\n int ln_blocks = B * N * S;\n \n AT_DISPATCH_FLOATING_TYPES(msa_permuted.scalar_type(), \"layer_norm_kernel\", ([&] {\n layer_norm_kernel<scalar_t><<<ln_blocks, ln_threads, 0, stream>>>(\n msa_permuted.data_ptr<scalar_t>(),\n ln_out.data_ptr<scalar_t>(),\n ln_weight.data_ptr<scalar_t>(),\n ln_bias.data_ptr<scalar_t>(),\n B * N * S, D, 1e-5f);\n }));\n \n // Prepare Q, K, V, G\n auto q_out = torch::empty({B, N, h, S, d}, msa_repr.options());\n auto k_out = torch::empty({B, N, h, S, d}, msa_repr.options());\n auto v_out = torch::empty({B, N, h, S, d}, msa_repr.options());\n auto g_proj = torch::empty({B, N, S, D}, msa_repr.options());\n \n int prep_threads = std::min(h * d, 256);\n int prep_blocks = B * N * S;\n size_t prep_shared = D * sizeof(float);\n \n AT_DISPATCH_FLOATING_TYPES(msa_permuted.scalar_type(), \"prepare_qkv_kernel\", ([&] {\n prepare_qkv_kernel<scalar_t><<<prep_blocks, prep_threads, prep_shared, stream>>>(\n ln_out.data_ptr<scalar_t>(),\n q_weight.data_ptr<scalar_t>(),\n k_weight.data_ptr<scalar_t>(),\n v_weight.data_ptr<scalar_t>(),\n g_weight.data_ptr<scalar_t>(),\n g_bias.data_ptr<scalar_t>(),\n q_out.data_ptr<scalar_t>(),\n k_out.data_ptr<scalar_t>(),\n v_out.data_ptr<scalar_t>(),\n g_proj.data_ptr<scalar_t>(),\n B, N, S, D, h, d);\n }));\n \n // Compute attention: Q @ K^T\n // q_out: (B, N, h, S, d), k_out: (B, N, h, S, d)\n // Result: (B, N, h, S, S)\n \n // Reshape for batched matmul: (B*N*h, S, d)\n auto q_reshaped = q_out.view({B * N * h, S, d});\n auto k_reshaped = k_out.view({B * N * h, S, d});\n \n // attention = q @ k^T\n auto attn = torch::matmul(q_reshaped, k_reshaped.transpose(1, 2));\n attn = attn.view({B, N, h, S, S});\n \n // Apply scale\n attn = attn * scale;\n \n // Apply mask and softmax\n // msa_mask: (B, S, N) -> col_mask: (B, N, S)\n auto col_mask = msa_mask.permute({0, 2, 1}).contiguous();\n \n int softmax_threads = std::min(S, 256);\n int softmax_blocks = B * N * h * S;\n \n AT_DISPATCH_FLOATING_TYPES(attn.scalar_type(), \"softmax_mask_kernel\", ([&] {\n softmax_mask_kernel<scalar_t><<<softmax_blocks, softmax_threads, 0, stream>>>(\n attn.data_ptr<scalar_t>(),\n col_mask.to(at::kInt).data_ptr<int>(),\n B, N, h, S);\n }));\n \n // Apply attention to values: attn @ v\n // attn: (B, N, h, S, S), v: (B, N, h, S, d)\n auto attn_reshaped = attn.view({B * N * h, S, S});\n auto v_reshaped = v_out.view({B * N * h, S, d});\n \n auto attn_out = torch::matmul(attn_reshaped, v_reshaped);\n attn_out = attn_out.view({B, N, h, S, d});\n \n // Fused gate + output projection\n auto output = torch::empty({B, S, N, D}, msa_repr.options());\n \n int fused_threads = std::min(D, 256);\n int fused_blocks = B * N * S;\n size_t fused_shared = 2 * D * sizeof(float);\n \n AT_DISPATCH_FLOATING_TYPES(msa_permuted.scalar_type(), \"fused_gate_out_kernel\", ([&] {\n fused_gate_out_kernel<scalar_t><<<fused_blocks, fused_threads, fused_shared, stream>>>(\n g_proj.data_ptr<scalar_t>(),\n attn_out.data_ptr<scalar_t>(),\n out_weight.data_ptr<scalar_t>(),\n out_bias.defined() ? out_bias.data_ptr<scalar_t>() : nullptr,\n output.data_ptr<scalar_t>(),\n B, N, S, D, h, d);\n }));\n \n return {output};\n}\n\"\"\"\n\n# Load the CUDA extension\ntry:\n msa_attention_ext = load_inline(\n name='msa_attention_ext',\n cpp_sources='',\n cuda_sources=cuda_source,\n functions=['msa_attention_cuda'],\n verbose=True,\n extra_cuda_cflags=['-O3', '--use_fast_math']\n )\nexcept Exception as e:\n print(f\"Failed to load CUDA extension: {e}\")\n msa_attention_ext = None\n\n\nclass ModelNew(nn.Module):\n \"\"\"\n Column-wise gated self-attention over MSA representations with CUDA kernels.\n \"\"\"\n\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)\n \n self.msa_dim = msa_dim\n\n def forward(self, msa_repr: torch.Tensor, msa_mask: torch.Tensor) -> torch.Tensor:\n B, S, N, D = msa_repr.shape\n h = self.num_heads\n d = self.head_dim\n \n # Use custom CUDA kernel if available and on CUDA\n if msa_attention_ext is not None and msa_repr.is_cuda:\n scale = 1.0 / math.sqrt(d)\n result = msa_attention_ext.msa_attention_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 scale\n )\n out = result[0]\n # Apply dropout (only during training)\n if self.training and self.dropout.p > 0:\n out = self.dropout(out)\n return out\n \n # Fallback to PyTorch implementation\n x = msa_repr.permute(0, 2, 1, 3) # (B, N, S, D)\n x = self.layer_norm(x)\n\n q = self.query(x).view(B, N, S, h, d).permute(0, 1, 3, 2, 4)\n k = self.key(x).view(B, N, S, h, d).permute(0, 1, 3, 2, 4)\n v = self.value(x).view(B, N, S, h, d).permute(0, 1, 3, 2, 4)\n g = torch.sigmoid(self.gate(x)).view(B, N, S, h, d).permute(0, 1, 3, 2, 4)\n\n attn = torch.einsum(\"bnhid,bnhjd->bnhij\", q, k) / math.sqrt(d)\n\n col_mask = msa_mask.permute(0, 2, 1) # (B, N, S)\n mask = col_mask.view(B, N, 1, 1, S).expand_as(attn)\n attn = attn.masked_fill(mask == 0, float(\"-inf\"))\n attn = F.softmax(attn, dim=-1)\n attn = self.dropout(attn)\n\n out = torch.einsum(\"bnhij,bnhjd->bnhid\", attn, v)\n out = g * out\n out = out.permute(0, 1, 3, 2, 4).reshape(B, N, S, D)\n out = self.out_proj(out)\n return out.permute(0, 2, 1, 3) # back to (B, S, N, D)"
}