call → compile_kernel
callcompile_kernel
{
"kernel_code": "import math\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.utils.cpp_extension import load_inline\n\n# Compile the CUDA kernel\ncuda_source = \"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <cmath>\n\n// RoPE kernel: applies rotary position embeddings\n// x: (B, H, L, d) - input tensor\n// cos, sin: (L, d/2) - position embeddings\n// out: (B, H, L, d) - output tensor\n__global__ void rope_kernel(\n const float* __restrict__ x_ptr,\n const float* __restrict__ cos_ptr,\n const float* __restrict__ sin_ptr,\n float* __restrict__ out_ptr,\n int B, int H, int L, int d\n) {\n int half_d = d / 2;\n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n int total = B * H * L * half_d;\n \n if (idx < total) {\n int b = idx / (H * L * half_d);\n int h = (idx / (L * half_d)) % H;\n int l = (idx / half_d) % L;\n int i = idx % half_d;\n \n int base_idx = b * H * L * d + h * L * d + l * d;\n float x1 = x_ptr[base_idx + i];\n float x2 = x_ptr[base_idx + i + half_d];\n float c = cos_ptr[l * half_d + i];\n float s = sin_ptr[l * half_d + i];\n \n out_ptr[base_idx + i] = x1 * c - x2 * s;\n out_ptr[base_idx + i + half_d] = x1 * s + x2 * c;\n }\n}\n\n// Softmax kernel for attention weights\n// attn: (B, H, T_q, T_k) - attention scores\n// scale: scaling factor\n__global__ void softmax_kernel(\n float* __restrict__ attn_ptr,\n int B, int H, int T_q, int T_k,\n float scale\n) {\n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n int total = B * H * T_q;\n \n if (idx < total) {\n int b = idx / (H * T_q);\n int h = (idx / T_q) % H;\n int q_idx = idx % T_q;\n \n int row_offset = b * H * T_q * T_k + h * T_q * T_k + q_idx * T_k;\n \n // Find max for numerical stability\n float max_val = -INFINITY;\n for (int k_idx = 0; k_idx < T_k; k_idx++) {\n float val = attn_ptr[row_offset + k_idx] * scale;\n if (val > max_val) max_val = val;\n }\n \n // Compute exp and sum\n float sum = 0.0f;\n for (int k_idx = 0; k_idx < T_k; k_idx++) {\n float val = expf(attn_ptr[row_offset + k_idx] * scale - max_val);\n attn_ptr[row_offset + k_idx] = val;\n sum += val;\n }\n \n // Normalize\n float inv_sum = 1.0f / sum;\n for (int k_idx = 0; k_idx < T_k; k_idx++) {\n attn_ptr[row_offset + k_idx] *= inv_sum;\n }\n }\n}\n\n// Attention matrix multiplication: Q @ K^T\n__global__ void qk_kernel(\n const float* __restrict__ q_ptr,\n const float* __restrict__ k_ptr,\n float* __restrict__ attn_ptr,\n int B, int H, int T_q, int T_k, int d\n) {\n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n int total = B * H * T_q * T_k;\n \n if (idx < total) {\n int b = idx / (H * T_q * T_k);\n int h = (idx / (T_q * T_k)) % H;\n int q_idx = (idx / T_k) % T_q;\n int kv_idx = idx % T_k;\n \n int q_offset = b * H * T_q * d + h * T_q * d + q_idx * d;\n int k_offset = b * H * T_k * d + h * T_k * d + kv_idx * d;\n \n float sum = 0.0f;\n for (int i = 0; i < d; i++) {\n sum += q_ptr[q_offset + i] * k_ptr[k_offset + i];\n }\n \n attn_ptr[idx] = sum;\n }\n}\n\n// Attention-value multiplication: attn @ V\n__global__ void av_kernel(\n const float* __restrict__ attn_ptr,\n const float* __restrict__ v_ptr,\n float* __restrict__ out_ptr,\n int B, int H, int T_q, int T_k, int d\n) {\n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n int total = B * H * T_q * d;\n \n if (idx < total) {\n int b = idx / (H * T_q * d);\n int h = (idx / (T_q * d)) % H;\n int q_idx = (idx / d) % T_q;\n int i = idx % d;\n \n int attn_offset = b * H * T_q * T_k + h * T_q * T_k + q_idx * T_k;\n \n float sum = 0.0f;\n for (int kv_idx = 0; kv_idx < T_k; kv_idx++) {\n int v_offset = b * H * T_k * d + h * T_k * d + kv_idx * d + i;\n sum += attn_ptr[attn_offset + kv_idx] * v_ptr[v_offset];\n }\n \n out_ptr[idx] = sum;\n }\n}\n\n// Host functions\nvoid rope_cuda(\n const torch::Tensor& x,\n const torch::Tensor& cos,\n const torch::Tensor& sin,\n torch::Tensor& out\n) {\n int B = x.size(0);\n int H = x.size(1);\n int L = x.size(2);\n int d = x.size(3);\n \n int total = B * H * L * (d / 2);\n int threads = 256;\n int blocks = (total + threads - 1) / threads;\n \n rope_kernel<<<blocks, threads>>>(\n x.data_ptr<float>(),\n cos.data_ptr<float>(),\n sin.data_ptr<float>(),\n out.data_ptr<float>(),\n B, H, L, d\n );\n}\n\nvoid softmax_cuda(\n torch::Tensor& attn,\n int B, int H, int T_q, int T_k,\n float scale\n) {\n int total = B * H * T_q;\n int threads = 256;\n int blocks = (total + threads - 1) / threads;\n \n softmax_kernel<<<blocks, threads>>>(\n attn.data_ptr<float>(),\n B, H, T_q, T_k, scale\n );\n}\n\nvoid qk_cuda(\n const torch::Tensor& q,\n const torch::Tensor& k,\n torch::Tensor& attn,\n int B, int H, int T_q, int T_k, int d\n) {\n int total = B * H * T_q * T_k;\n int threads = 256;\n int blocks = (total + threads - 1) / threads;\n \n qk_kernel<<<blocks, threads>>>(\n q.data_ptr<float>(),\n k.data_ptr<float>(),\n attn.data_ptr<float>(),\n B, H, T_q, T_k, d\n );\n}\n\nvoid av_cuda(\n const torch::Tensor& attn,\n const torch::Tensor& v,\n torch::Tensor& out,\n int B, int H, int T_q, int T_k, int d\n) {\n int total = B * H * T_q * d;\n int threads = 256;\n int blocks = (total + threads - 1) / threads;\n \n av_kernel<<<blocks, threads>>>(\n attn.data_ptr<float>(),\n v.data_ptr<float>(),\n out.data_ptr<float>(),\n B, H, T_q, T_k, d\n );\n}\n\"\"\"\n\ncpp_source = \"\"\"\nvoid rope_cuda(const torch::Tensor& x, const torch::Tensor& cos, const torch::Tensor& sin, torch::Tensor& out);\nvoid softmax_cuda(torch::Tensor& attn, int B, int H, int T_q, int T_k, float scale);\nvoid qk_cuda(const torch::Tensor& q, const torch::Tensor& k, torch::Tensor& attn, int B, int H, int T_q, int T_k, int d);\nvoid av_cuda(const torch::Tensor& attn, const torch::Tensor& v, torch::Tensor& out, int B, int H, int T_q, int T_k, int d);\n\"\"\"\n\n# Load the extension\nattention_ext = load_inline(\n name='attention_ext',\n cpp_sources=cpp_source,\n cuda_sources=cuda_source,\n functions=['rope_cuda', 'softmax_cuda', 'qk_cuda', 'av_cuda'],\n extra_cuda_cflags=['-O3'],\n verbose=False\n)\n\n\nclass Model(nn.Module):\n \"\"\"\n Single-layer multi-head cross-attention with RoPE on Q and K, attention\n dropout, and output projection\u2014intended as a fusion target for robotics\n stacks (e.g. proprio / action queries attending to vision or memory).\n \"\"\"\n\n def __init__(\n self,\n dim: int,\n num_heads: int,\n dropout: float = 0.0,\n rope_base: float = 10000.0,\n ):\n super().__init__()\n assert dim % num_heads == 0\n self.dim = dim\n self.num_heads = num_heads\n self.head_dim = dim // num_heads\n assert self.head_dim % 2 == 0, \"RoPE requires an even head dimension\"\n\n self.q_proj = nn.Linear(dim, dim, bias=False)\n self.k_proj = nn.Linear(dim, dim, bias=False)\n self.v_proj = nn.Linear(dim, dim, bias=False)\n self.out_proj = nn.Linear(dim, dim, bias=False)\n self.dropout = nn.Dropout(dropout)\n self.scale = 1.0 / math.sqrt(self.head_dim)\n\n inv_freq = 1.0 / (\n rope_base ** (torch.arange(0, self.head_dim, 2).float() / self.head_dim)\n )\n self.register_buffer(\"inv_freq\", inv_freq, persistent=False)\n\n @staticmethod\n def _rotate_half(x: torch.Tensor) -> torch.Tensor:\n x1, x2 = x.chunk(2, dim=-1)\n return torch.cat([-x2, x1], dim=-1)\n\n def _rope(self, x: torch.Tensor, seq_len: int) -> torch.Tensor:\n \"\"\"Apply RoPE along the sequence dimension (last-but-one after head). x: (B, H, L, d).\"\"\"\n t = torch.arange(seq_len, device=x.device, dtype=self.inv_freq.dtype)\n freqs = torch.outer(t, self.inv_freq)\n emb = torch.cat([freqs, freqs], dim=-1)\n cos = emb.cos().view(1, 1, seq_len, self.head_dim)\n sin = emb.sin().view(1, 1, seq_len, self.head_dim)\n return x * cos + self._rotate_half(x) * sin\n\n def forward(self, x_query: torch.Tensor, x_kv: torch.Tensor) -> torch.Tensor:\n B, T_q, _ = x_query.shape\n _, T_k, _ = x_kv.shape\n h, d = self.num_heads, self.head_dim\n\n q = self.q_proj(x_query).view(B, T_q, h, d).transpose(1, 2)\n k = self.k_proj(x_kv).view(B, T_k, h, d).transpose(1, 2)\n v = self.v_proj(x_kv).view(B, T_k, h, d).transpose(1, 2)\n\n q = self._rope(q, T_q)\n k = self._rope(k, T_k)\n\n attn = torch.matmul(q, k.transpose(-2, -1)) * self.scale\n attn = F.softmax(attn, dim=-1)\n attn = self.dropout(attn)\n out = torch.matmul(attn, v)\n out = out.transpose(1, 2).contiguous().view(B, T_q, self.dim)\n return self.out_proj(out)\n\n\nclass ModelNew(nn.Module):\n \"\"\"\n Custom CUDA implementation of multi-head cross-attention with RoPE.\n \"\"\"\n\n def __init__(\n self,\n dim: int,\n num_heads: int,\n dropout: float = 0.0,\n rope_base: float = 10000.0,\n ):\n super().__init__()\n assert dim % num_heads == 0\n self.dim = dim\n self.num_heads = num_heads\n self.head_dim = dim // num_heads\n assert self.head_dim % 2 == 0, \"RoPE requires an even head dimension\"\n\n self.q_proj = nn.Linear(dim, dim, bias=False)\n self.k_proj = nn.Linear(dim, dim, bias=False)\n self.v_proj = nn.Linear(dim, dim, bias=False)\n self.out_proj = nn.Linear(dim, dim, bias=False)\n self.dropout = nn.Dropout(dropout)\n self.scale = 1.0 / math.sqrt(self.head_dim)\n\n inv_freq = 1.0 / (\n rope_base ** (torch.arange(0, self.head_dim, 2).float() / self.head_dim)\n )\n self.register_buffer(\"inv_freq\", inv_freq, persistent=False)\n\n def _compute_rope_emb(self, seq_len: int, device):\n \"\"\"Compute RoPE embeddings for a sequence length.\"\"\"\n t = torch.arange(seq_len, device=device, dtype=self.inv_freq.dtype)\n freqs = torch.outer(t, self.inv_freq)\n cos = freqs.cos() # (seq_len, head_dim/2)\n sin = freqs.sin() # (seq_len, head_dim/2)\n return cos, sin\n\n def forward(self, x_query: torch.Tensor, x_kv: torch.Tensor) -> torch.Tensor:\n B, T_q, _ = x_query.shape\n _, T_k, _ = x_kv.shape\n h, d = self.num_heads, self.head_dim\n\n # Project and reshape\n q = self.q_proj(x_query).view(B, T_q, h, d).transpose(1, 2).contiguous()\n k = self.k_proj(x_kv).view(B, T_k, h, d).transpose(1, 2).contiguous()\n v = self.v_proj(x_kv).view(B, T_k, h, d).transpose(1, 2).contiguous()\n\n # Compute RoPE embeddings\n cos_q, sin_q = self._compute_rope_emb(T_q, x_query.device)\n cos_k, sin_k = self._compute_rope_emb(T_k, x_kv.device)\n\n # Apply RoPE using custom CUDA kernel\n q_rope = torch.empty_like(q)\n k_rope = torch.empty_like(k)\n attention_ext.rope_cuda(q, cos_q, sin_q, q_rope)\n attention_ext.rope_cuda(k, cos_k, sin_k, k_rope)\n\n # Compute attention scores using custom CUDA kernel\n attn = torch.empty(B, h, T_q, T_k, device=x_query.device, dtype=x_query.dtype)\n attention_ext.qk_cuda(q_rope, k_rope, attn, B, h, T_q, T_k, d)\n\n # Apply softmax using custom CUDA kernel\n attention_ext.softmax_cuda(attn, B, h, T_q, T_k, self.scale)\n\n # Apply dropout (p=0 in test, so no-op)\n attn = self.dropout(attn)\n\n # Compute attention output using custom CUDA kernel\n attn_out = torch.empty(B, h, T_q, d, device=x_query.device, dtype=x_query.dtype)\n attention_ext.av_cuda(attn, v, attn_out, B, h, T_q, T_k, d)\n\n # Transpose and reshape for output projection\n out = attn_out.transpose(1, 2).contiguous().view(B, T_q, self.dim)\n\n return self.out_proj(out)\n\n\nbatch_size = 4\nseq_len_q = 64\nseq_len_kv = 128\ndim = 256\nnum_heads = 8\ndropout_p = 0.0\n\n\ndef get_inputs():\n x_query = torch.randn(batch_size, seq_len_q, dim)\n x_kv = torch.randn(batch_size, seq_len_kv, dim)\n return [x_query, x_kv]\n\n\ndef get_init_inputs():\n return [dim, num_heads, dropout_p]"
}