correct
20_RotaryPositionEmbeddingBio.py
started
2026-05-11T14:18:12Z
timingkernelbench.timing
kernel mean / median / std
0.02 / — / 5.11e-04 μs
ref mean / median / std
0.11 / — / 0.08 μs
numerical precisioneval_kernel_against_ref
gpu memoryextended_metrics.measure_memory
kernel launches / fusiontorch.profiler
fusion ratio (ref / kernel)
4.09x
SOL (speed-of-light)nsight_hardware_counters
roofline / occupancynsight_hardware_counters
dram bandwidth
1286.28 GB/s
warp stalls (top 5)nsight
reference PyTorch source (the prompt the agent sees)
import torch
import torch.nn as nn
import math
class Model(nn.Module):
"""
Rotary Position Embedding (RoPE) applied to protein residue sequences.
Used in ESM-2 and similar protein language models. Encodes absolute
position via rotation of query/key pairs in 2-D subspaces, preserving
relative-position information in dot-product attention.
"""
def __init__(self, dim, max_seq_len=2048, base=10000.0):
super().__init__()
inv_freq = 1.0 / (base ** (torch.arange(0, dim, 2).float() / dim))
self.register_buffer("inv_freq", inv_freq)
self.max_seq_len = max_seq_len
self.dim = dim
def _build_cos_sin(self, seq_len: int, device: torch.device):
t = torch.arange(seq_len, device=device, dtype=self.inv_freq.dtype)
freqs = torch.outer(t, self.inv_freq)
emb = torch.cat([freqs, freqs], dim=-1)
return emb.cos(), emb.sin()
@staticmethod
def _rotate_half(x: torch.Tensor) -> torch.Tensor:
x1, x2 = x.chunk(2, dim=-1)
return torch.cat([-x2, x1], dim=-1)
def forward(self, q: torch.Tensor, k: torch.Tensor) -> tuple:
"""
Args:
q: (B, H, N, D) – queries
k: (B, H, N, D) – keys
Returns:
(q_rot, k_rot) each (B, H, N, D) with RoPE applied
"""
N = q.shape[2]
cos, sin = self._build_cos_sin(N, q.device) # (N, D)
cos = cos.unsqueeze(0).unsqueeze(0)
sin = sin.unsqueeze(0).unsqueeze(0)
q_rot = q * cos + self._rotate_half(q) * sin
k_rot = k * cos + self._rotate_half(k) * sin
return q_rot, k_rot
dim = 64
num_heads = 8
seq_len = 256
batch_size = 4
def get_inputs():
p = popcorn_pri
bs = p.jitter_int(batch_size)
nh = p.jitter_int(num_heads)
sl = p.jitter_int(seq_len)
q = torch.randn(bs, nh, sl, dim)
k = torch.randn(bs, nh, sl, dim)
return [q, k]
def get_init_inputs():
return [dim]
turn 0363.7s turn · 2.9s LLM · 360.8s tools · tokens in=1,775 · out=119 · total=1,894
reasoning
Let me analyze the task. I need to implement a Rotary Position Embedding (RoPE) kernel in CUDA that:
1. Takes query and key tensors of shape (B, H, N, D)
2. Builds cos/sin tables based on sequence length
3. Applies rotation: q_rot = q * cos + rotate_half(q) * sin
The rotation operation:
- split x into x1, x2 along last dimension
- return concat([-x2, x1])
Let me first get GPU specs to understand the hardware constraints.
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 1 · 1 tool call(s) · 274.9s turn · 16.0s LLM · tokens in=2,023 · out=1,545 · total=3,568 · 6,812 chars (click to expand)
turn 1274.9s turn · 16.0s LLM · 258.8s tools · tokens in=2,023 · out=1,545 · total=3,568
reasoning
Now let me write an initial CUDA kernel for the RoPE operation. The key operations are:
1. Build cos/sin tables (N, D) from inv_freq
2. Broadcast to (1, 1, N, D) and apply rotation
The rotation formula is:
q_rot = q * cos + rotate_half(q) * sin
where rotate_half concatenates [-x2, x1] after splitting x into [x1, x2].
Let me write a fused kernel that:
1. Pre-computes cos/sin tables (or loads them if precomputed)
2. Applies the rotation to both q and k
I'll start with a straightforward implementation and optimize from there.
assistantLet me write an initial CUDA kernel implementation for RoPE. I'll fuse the cos/sin table computation and the rotation operation.
call → compile_kernel
callcompile_kernel
{
"kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\nimport math\n\ncuda_source = \"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <cmath>\n\n__device__ __forceinline__ float get_inv_freq(int i, int dim, float base) {\n // inv_freq[i] = 1.0 / (base ** (2*i / dim)) where i goes from 0 to dim/2-1\n float exponent = (2.0f * (float)i) / (float)dim;\n return 1.0f / powf(base, exponent);\n}\n\n__global__ void rope_kernel(\n const float* __restrict__ q,\n const float* __restrict__ k,\n float* __restrict__ q_out,\n float* __restrict__ k_out,\n const float* __restrict__ inv_freq,\n int batch_size, int num_heads, int seq_len, int dim, float base\n) {\n // Each thread handles one element of the output\n // Grid: (batch_size * num_heads * seq_len) blocks, each with (dim) threads\n // Or we can use 2D grid\n \n int b = blockIdx.z; // batch\n int h = blockIdx.y; // head\n int n = blockIdx.x; // sequence position\n \n if (b >= batch_size || h >= num_heads || n >= seq_len) return;\n \n int base_idx = ((b * num_heads + h) * seq_len + n) * dim;\n \n // Compute cos/sin for this position n\n // We need cos and sin for all dim/2 frequencies\n // Each thread handles one element\n \n for (int d = threadIdx.x; d < dim; d += blockDim.x) {\n int half_d = dim / 2;\n \n // Determine which frequency index and cos/sin value to use\n int freq_idx;\n float cos_val, sin_val;\n \n if (d < half_d) {\n // First half: use inv_freq[d], angle = n * inv_freq[d]\n freq_idx = d;\n } else {\n // Second half: use inv_freq[d - half_d], angle = n * inv_freq[d - half_d]\n freq_idx = d - half_d;\n }\n \n // Compute angle = n * inv_freq[freq_idx]\n float inv_f = inv_freq[freq_idx];\n float angle = (float)n * inv_f;\n cos_val = cosf(angle);\n sin_val = sinf(angle);\n \n // Apply rotation\n float q_val = q[base_idx + d];\n float k_val = k[base_idx + d];\n \n float q_rot, k_rot;\n \n if (d < half_d) {\n // x1 part: x1 * cos - x2 * sin\n // Need x2 value\n float q_x2 = q[base_idx + d + half_d];\n float k_x2 = k[base_idx + d + half_d];\n q_rot = q_val * cos_val - q_x2 * sin_val;\n k_rot = k_val * cos_val - k_x2 * sin_val;\n } else {\n // x2 part: x2 * cos + x1 * sin\n // Need x1 value\n float q_x1 = q[base_idx + d - half_d];\n float k_x1 = k[base_idx + d - half_d];\n q_rot = q_val * cos_val + q_x1 * sin_val;\n k_rot = k_val * cos_val + k_x1 * sin_val;\n }\n \n q_out[base_idx + d] = q_rot;\n k_out[base_idx + d] = k_rot;\n }\n}\n\ntorch::Tensor apply_rope_cuda(\n torch::Tensor q,\n torch::Tensor k,\n torch::Tensor inv_freq,\n int64_t max_seq_len,\n double base\n) {\n auto q_out = torch::empty_like(q);\n auto k_out = torch::empty_like(k);\n \n int batch_size = q.size(0);\n int num_heads = q.size(1);\n int seq_len = q.size(2);\n int dim = q.size(3);\n \n dim3 grid(seq_len, num_heads, batch_size);\n dim3 block(std::min(dim, 1024));\n \n rope_kernel<<<grid, block>>>(\n q.data_ptr<float>(),\n k.data_ptr<float>(),\n q_out.data_ptr<float>(),\n k_out.data_ptr<float>(),\n inv_freq.data_ptr<float>(),\n batch_size, num_heads, seq_len, dim, (float)base\n );\n \n return torch::stack({q_out, k_out}, 0);\n}\n\"\"\"\n\ncpp_source = \"\"\"\ntorch::Tensor apply_rope_cuda(torch::Tensor q, torch::Tensor k, torch::Tensor inv_freq, int64_t max_seq_len, double base);\n\"\"\"\n\nrope_extension = load_inline(\n name='rope_extension',\n cpp_sources=cpp_source,\n cuda_sources=cuda_source,\n functions=['apply_rope_cuda'],\n extra_cuda_cflags=['-O3'],\n verbose=False\n)\n\n\nclass ModelNew(nn.Module):\n \"\"\"\n Rotary Position Embedding (RoPE) applied to protein residue sequences.\n Optimized CUDA implementation.\n \"\"\"\n\n def __init__(self, dim, max_seq_len=2048, base=10000.0):\n super().__init__()\n inv_freq = 1.0 / (base ** (torch.arange(0, dim, 2).float() / dim))\n self.register_buffer(\"inv_freq\", inv_freq)\n self.max_seq_len = max_seq_len\n self.dim = dim\n self.base = base\n\n def forward(self, q: torch.Tensor, k: torch.Tensor) -> tuple:\n \"\"\"\n Args:\n q: (B, H, N, D) \u2013 queries\n k: (B, H, N, D) \u2013 keys\n Returns:\n (q_rot, k_rot) each (B, H, N, D) with RoPE applied\n \"\"\"\n result = rope_extension.apply_rope_cuda(\n q.contiguous(), \n k.contiguous(), \n self.inv_freq,\n self.max_seq_len,\n self.base\n )\n return result[0], result[1]"
}
result ← compile_kernel (ok)
okcompile_kernel
compile_kernel PASSED: kernel compiled without errors.
turn 2 · 1 tool call(s) · 577.8s turn · 11.9s LLM · tokens in=3,437 · out=1,389 · total=4,826 · 6,240 chars (click to expand)
turn 2577.8s turn · 11.9s LLM · 565.9s tools · tokens in=3,437 · out=1,389 · total=4,826
reasoning
The kernel compiled successfully. Now let me run the correctness check.
call → run_correctness
callrun_correctness
{
"kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\nimport math\n\ncuda_source = \"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <cmath>\n\n__device__ __forceinline__ float get_inv_freq(int i, int dim, float base) {\n // inv_freq[i] = 1.0 / (base ** (2*i / dim)) where i goes from 0 to dim/2-1\n float exponent = (2.0f * (float)i) / (float)dim;\n return 1.0f / powf(base, exponent);\n}\n\n__global__ void rope_kernel(\n const float* __restrict__ q,\n const float* __restrict__ k,\n float* __restrict__ q_out,\n float* __restrict__ k_out,\n const float* __restrict__ inv_freq,\n int batch_size, int num_heads, int seq_len, int dim, float base\n) {\n // Each thread handles one element of the output\n // Grid: (batch_size * num_heads * seq_len) blocks, each with (dim) threads\n // Or we can use 2D grid\n \n int b = blockIdx.z; // batch\n int h = blockIdx.y; // head\n int n = blockIdx.x; // sequence position\n \n if (b >= batch_size || h >= num_heads || n >= seq_len) return;\n \n int base_idx = ((b * num_heads + h) * seq_len + n) * dim;\n \n // Compute cos/sin for this position n\n // We need cos and sin for all dim/2 frequencies\n // Each thread handles one element\n \n for (int d = threadIdx.x; d < dim; d += blockDim.x) {\n int half_d = dim / 2;\n \n // Determine which frequency index and cos/sin value to use\n int freq_idx;\n float cos_val, sin_val;\n \n if (d < half_d) {\n // First half: use inv_freq[d], angle = n * inv_freq[d]\n freq_idx = d;\n } else {\n // Second half: use inv_freq[d - half_d], angle = n * inv_freq[d - half_d]\n freq_idx = d - half_d;\n }\n \n // Compute angle = n * inv_freq[freq_idx]\n float inv_f = inv_freq[freq_idx];\n float angle = (float)n * inv_f;\n cos_val = cosf(angle);\n sin_val = sinf(angle);\n \n // Apply rotation\n float q_val = q[base_idx + d];\n float k_val = k[base_idx + d];\n \n float q_rot, k_rot;\n \n if (d < half_d) {\n // x1 part: x1 * cos - x2 * sin\n // Need x2 value\n float q_x2 = q[base_idx + d + half_d];\n float k_x2 = k[base_idx + d + half_d];\n q_rot = q_val * cos_val - q_x2 * sin_val;\n k_rot = k_val * cos_val - k_x2 * sin_val;\n } else {\n // x2 part: x2 * cos + x1 * sin\n // Need x1 value\n float q_x1 = q[base_idx + d - half_d];\n float k_x1 = k[base_idx + d - half_d];\n q_rot = q_val * cos_val + q_x1 * sin_val;\n k_rot = k_val * cos_val + k_x1 * sin_val;\n }\n \n q_out[base_idx + d] = q_rot;\n k_out[base_idx + d] = k_rot;\n }\n}\n\ntorch::Tensor apply_rope_cuda(\n torch::Tensor q,\n torch::Tensor k,\n torch::Tensor inv_freq,\n int64_t max_seq_len,\n double base\n) {\n auto q_out = torch::empty_like(q);\n auto k_out = torch::empty_like(k);\n \n int batch_size = q.size(0);\n int num_heads = q.size(1);\n int seq_len = q.size(2);\n int dim = q.size(3);\n \n dim3 grid(seq_len, num_heads, batch_size);\n dim3 block(std::min(dim, 1024));\n \n rope_kernel<<<grid, block>>>(\n q.data_ptr<float>(),\n k.data_ptr<float>(),\n q_out.data_ptr<float>(),\n k_out.data_ptr<float>(),\n inv_freq.data_ptr<float>(),\n batch_size, num_heads, seq_len, dim, (float)base\n );\n \n return torch::stack({q_out, k_out}, 0);\n}\n\"\"\"\n\ncpp_source = \"\"\"\ntorch::Tensor apply_rope_cuda(torch::Tensor q, torch::Tensor k, torch::Tensor inv_freq, int64_t max_seq_len, double base);\n\"\"\"\n\nrope_extension = load_inline(\n name='rope_extension',\n cpp_sources=cpp_source,\n cuda_sources=cuda_source,\n functions=['apply_rope_cuda'],\n extra_cuda_cflags=['-O3'],\n verbose=False\n)\n\n\nclass ModelNew(nn.Module):\n \"\"\"\n Rotary Position Embedding (RoPE) applied to protein residue sequences.\n Optimized CUDA implementation.\n \"\"\"\n\n def __init__(self, dim, max_seq_len=2048, base=10000.0):\n super().__init__()\n inv_freq = 1.0 / (base ** (torch.arange(0, dim, 2).float() / dim))\n self.register_buffer(\"inv_freq\", inv_freq)\n self.max_seq_len = max_seq_len\n self.dim = dim\n self.base = base\n\n def forward(self, q: torch.Tensor, k: torch.Tensor) -> tuple:\n \"\"\"\n Args:\n q: (B, H, N, D) \u2013 queries\n k: (B, H, N, D) \u2013 keys\n Returns:\n (q_rot, k_rot) each (B, H, N, D) with RoPE applied\n \"\"\"\n result = rope_extension.apply_rope_cuda(\n q.contiguous(), \n k.contiguous(), \n self.inv_freq,\n self.max_seq_len,\n self.base\n )\n return result[0], result[1]"
}
result ← run_correctness (ok)
okrun_correctness
run_correctness PASSED: (5 / 5) trials all matched the reference.
Numerical precision: max_abs_err=4.77e-07 mean_abs_err=1.43e-08 max_rel_err=8.85e-03
turn 3 · 1 tool call(s) · 273.5s turn · 13.0s LLM · tokens in=4,870 · out=1,387 · total=6,257 · 6,302 chars (click to expand)
turn 3273.5s turn · 13.0s LLM · 260.4s tools · tokens in=4,870 · out=1,387 · total=6,257
reasoning
The kernel passes correctness. Now let me optimize it further. The current implementation:
1. Each thread block handles one (batch, head, seq_pos) tuple
2. Threads in the block iterate over the dim dimension
3. Each thread computes cos/sin values which is redundant
Optimizations I can apply:
1. Precompute cos/sin tables for all positions and store in shared memory
2. Use vectorized loads/stores for better memory bandwidth
3. Reduce the number of cos/sin computations by having threads cooperate
4. Use a more efficient grid/block arrangement
Let me try a version where we precompute cos/sin in shared memory within each block.
assistantThe kernel passes correctness. Let me optimize it further by using shared memory to compute cos/sin values once per position, and use vectorized memory access.
call → compile_kernel
callcompile_kernel
{
"kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\nimport math\n\ncuda_source = \"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <cmath>\n\n__global__ void rope_kernel(\n const float* __restrict__ q,\n const float* __restrict__ k,\n float* __restrict__ q_out,\n float* __restrict__ k_out,\n const float* __restrict__ inv_freq,\n int batch_size, int num_heads, int seq_len, int dim\n) {\n // Each thread handles one (b, h, n, d) element\n // Flatten: total elements = batch_size * num_heads * seq_len * dim\n \n int half_dim = dim / 2;\n int total = batch_size * num_heads * seq_len * dim;\n \n for (int idx = blockIdx.x * blockDim.x + threadIdx.x; idx < total; idx += blockDim.x * gridDim.x) {\n int d = idx % dim;\n int n = (idx / dim) % seq_len;\n int h = (idx / (dim * seq_len)) % num_heads;\n int b = idx / (dim * seq_len * num_heads);\n \n int base_idx = ((b * num_heads + h) * seq_len + n) * dim;\n \n // Determine frequency index\n int freq_idx = (d < half_dim) ? d : (d - half_dim);\n \n // Compute angle = n * inv_freq[freq_idx]\n float inv_f = inv_freq[freq_idx];\n float angle = (float)n * inv_f;\n float cos_val = cosf(angle);\n float sin_val = sinf(angle);\n \n // Apply rotation\n if (d < half_dim) {\n // x1 part: x1 * cos - x2 * sin\n float q_x1 = q[base_idx + d];\n float k_x1 = k[base_idx + d];\n float q_x2 = q[base_idx + d + half_dim];\n float k_x2 = k[base_idx + d + half_dim];\n q_out[base_idx + d] = q_x1 * cos_val - q_x2 * sin_val;\n k_out[base_idx + d] = k_x1 * cos_val - k_x2 * sin_val;\n } else {\n // x2 part: x2 * cos + x1 * sin\n float q_x2 = q[base_idx + d];\n float k_x2 = k[base_idx + d];\n float q_x1 = q[base_idx + d - half_dim];\n float k_x1 = k[base_idx + d - half_dim];\n q_out[base_idx + d] = q_x2 * cos_val + q_x1 * sin_val;\n k_out[base_idx + d] = k_x2 * cos_val + k_x1 * sin_val;\n }\n }\n}\n\ntorch::Tensor apply_rope_cuda(\n torch::Tensor q,\n torch::Tensor k,\n torch::Tensor inv_freq,\n int64_t max_seq_len,\n double base\n) {\n auto q_out = torch::empty_like(q);\n auto k_out = torch::empty_like(k);\n \n int batch_size = q.size(0);\n int num_heads = q.size(1);\n int seq_len = q.size(2);\n int dim = q.size(3);\n \n int total = batch_size * num_heads * seq_len * dim;\n int threads = 256;\n int blocks = (total + threads - 1) / threads;\n blocks = std::min(blocks, 65535);\n \n rope_kernel<<<blocks, threads>>>(\n q.data_ptr<float>(),\n k.data_ptr<float>(),\n q_out.data_ptr<float>(),\n k_out.data_ptr<float>(),\n inv_freq.data_ptr<float>(),\n batch_size, num_heads, seq_len, dim\n );\n \n return torch::stack({q_out, k_out}, 0);\n}\n\"\"\"\n\ncpp_source = \"\"\"\ntorch::Tensor apply_rope_cuda(torch::Tensor q, torch::Tensor k, torch::Tensor inv_freq, int64_t max_seq_len, double base);\n\"\"\"\n\nrope_extension = load_inline(\n name='rope_extension_v2',\n cpp_sources=cpp_source,\n cuda_sources=cuda_source,\n functions=['apply_rope_cuda'],\n extra_cuda_cflags=['-O3'],\n verbose=False\n)\n\n\nclass ModelNew(nn.Module):\n \"\"\"\n Rotary Position Embedding (RoPE) applied to protein residue sequences.\n Optimized CUDA implementation.\n \"\"\"\n\n def __init__(self, dim, max_seq_len=2048, base=10000.0):\n super().__init__()\n inv_freq = 1.0 / (base ** (torch.arange(0, dim, 2).float() / dim))\n self.register_buffer(\"inv_freq\", inv_freq)\n self.max_seq_len = max_seq_len\n self.dim = dim\n self.base = base\n\n def forward(self, q: torch.Tensor, k: torch.Tensor) -> tuple:\n \"\"\"\n Args:\n q: (B, H, N, D) \u2013 queries\n k: (B, H, N, D) \u2013 keys\n Returns:\n (q_rot, k_rot) each (B, H, N, D) with RoPE applied\n \"\"\"\n result = rope_extension.apply_rope_cuda(\n q.contiguous(), \n k.contiguous(), \n self.inv_freq,\n self.max_seq_len,\n self.base\n )\n return result[0], result[1]"
}
result ← compile_kernel (ok)
okcompile_kernel
compile_kernel PASSED: kernel compiled without errors.
turn 4285.0s turn · 10.3s LLM · 274.8s tools · tokens in=6,131 · out=1,224 · total=7,355
reasoning
Let me check correctness again.
call → run_correctness
callrun_correctness
{
"kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\nimport math\n\ncuda_source = \"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <cmath>\n\n__global__ void rope_kernel(\n const float* __restrict__ q,\n const float* __restrict__ k,\n float* __restrict__ q_out,\n float* __restrict__ k_out,\n const float* __restrict__ inv_freq,\n int batch_size, int num_heads, int seq_len, int dim\n) {\n // Each thread handles one (b, h, n, d) element\n // Flatten: total elements = batch_size * num_heads * seq_len * dim\n \n int half_dim = dim / 2;\n int total = batch_size * num_heads * seq_len * dim;\n \n for (int idx = blockIdx.x * blockDim.x + threadIdx.x; idx < total; idx += blockDim.x * gridDim.x) {\n int d = idx % dim;\n int n = (idx / dim) % seq_len;\n int h = (idx / (dim * seq_len)) % num_heads;\n int b = idx / (dim * seq_len * num_heads);\n \n int base_idx = ((b * num_heads + h) * seq_len + n) * dim;\n \n // Determine frequency index\n int freq_idx = (d < half_dim) ? d : (d - half_dim);\n \n // Compute angle = n * inv_freq[freq_idx]\n float inv_f = inv_freq[freq_idx];\n float angle = (float)n * inv_f;\n float cos_val = cosf(angle);\n float sin_val = sinf(angle);\n \n // Apply rotation\n if (d < half_dim) {\n // x1 part: x1 * cos - x2 * sin\n float q_x1 = q[base_idx + d];\n float k_x1 = k[base_idx + d];\n float q_x2 = q[base_idx + d + half_dim];\n float k_x2 = k[base_idx + d + half_dim];\n q_out[base_idx + d] = q_x1 * cos_val - q_x2 * sin_val;\n k_out[base_idx + d] = k_x1 * cos_val - k_x2 * sin_val;\n } else {\n // x2 part: x2 * cos + x1 * sin\n float q_x2 = q[base_idx + d];\n float k_x2 = k[base_idx + d];\n float q_x1 = q[base_idx + d - half_dim];\n float k_x1 = k[base_idx + d - half_dim];\n q_out[base_idx + d] = q_x2 * cos_val + q_x1 * sin_val;\n k_out[base_idx + d] = k_x2 * cos_val + k_x1 * sin_val;\n }\n }\n}\n\ntorch::Tensor apply_rope_cuda(\n torch::Tensor q,\n torch::Tensor k,\n torch::Tensor inv_freq,\n int64_t max_seq_len,\n double base\n) {\n auto q_out = torch::empty_like(q);\n auto k_out = torch::empty_like(k);\n \n int batch_size = q.size(0);\n int num_heads = q.size(1);\n int seq_len = q.size(2);\n int dim = q.size(3);\n \n int total = batch_size * num_heads * seq_len * dim;\n int threads = 256;\n int blocks = (total + threads - 1) / threads;\n blocks = std::min(blocks, 65535);\n \n rope_kernel<<<blocks, threads>>>(\n q.data_ptr<float>(),\n k.data_ptr<float>(),\n q_out.data_ptr<float>(),\n k_out.data_ptr<float>(),\n inv_freq.data_ptr<float>(),\n batch_size, num_heads, seq_len, dim\n );\n \n return torch::stack({q_out, k_out}, 0);\n}\n\"\"\"\n\ncpp_source = \"\"\"\ntorch::Tensor apply_rope_cuda(torch::Tensor q, torch::Tensor k, torch::Tensor inv_freq, int64_t max_seq_len, double base);\n\"\"\"\n\nrope_extension = load_inline(\n name='rope_extension_v2',\n cpp_sources=cpp_source,\n cuda_sources=cuda_source,\n functions=['apply_rope_cuda'],\n extra_cuda_cflags=['-O3'],\n verbose=False\n)\n\n\nclass ModelNew(nn.Module):\n \"\"\"\n Rotary Position Embedding (RoPE) applied to protein residue sequences.\n Optimized CUDA implementation.\n \"\"\"\n\n def __init__(self, dim, max_seq_len=2048, base=10000.0):\n super().__init__()\n inv_freq = 1.0 / (base ** (torch.arange(0, dim, 2).float() / dim))\n self.register_buffer(\"inv_freq\", inv_freq)\n self.max_seq_len = max_seq_len\n self.dim = dim\n self.base = base\n\n def forward(self, q: torch.Tensor, k: torch.Tensor) -> tuple:\n \"\"\"\n Args:\n q: (B, H, N, D) \u2013 queries\n k: (B, H, N, D) \u2013 keys\n Returns:\n (q_rot, k_rot) each (B, H, N, D) with RoPE applied\n \"\"\"\n result = rope_extension.apply_rope_cuda(\n q.contiguous(), \n k.contiguous(), \n self.inv_freq,\n self.max_seq_len,\n self.base\n )\n return result[0], result[1]"
}
result ← run_correctness (ok)
okrun_correctness
run_correctness PASSED: (5 / 5) trials all matched the reference.
Numerical precision: max_abs_err=4.77e-07 mean_abs_err=1.43e-08 max_rel_err=8.85e-03
turn 5 · 1 tool call(s) · 416.7s turn · 13.0s LLM · tokens in=7,406 · out=1,401 · total=8,807 · 6,342 chars (click to expand)
turn 5416.7s turn · 13.0s LLM · 403.7s tools · tokens in=7,406 · out=1,401 · total=8,807
reasoning
Let me try a more optimized version that uses shared memory to precompute cos/sin values per position, reducing redundant computation across heads and batches. Also, I can use float4 vectorized loads/stores.
assistantLet me try a more optimized version using shared memory to precompute cos/sin values per position:
call → compile_kernel
callcompile_kernel
{
"kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\nimport math\n\ncuda_source = \"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <cmath>\n\n__global__ void rope_kernel(\n const float* __restrict__ q,\n const float* __restrict__ k,\n float* __restrict__ q_out,\n float* __restrict__ k_out,\n const float* __restrict__ inv_freq,\n int batch_size, int num_heads, int seq_len, int dim\n) {\n // Use shared memory for cos/sin values per position\n // Each block handles one (n) position, with threads for different (b, h, d)\n int half_dim = dim / 2;\n \n // Shared memory for cos/sin values for this position\n extern __shared__ float shared_cos_sin[];\n float* cos_vals = shared_cos_sin;\n float* sin_vals = shared_cos_sin + half_dim;\n \n int n = blockIdx.x; // Each block handles one sequence position\n int tid = threadIdx.x;\n \n // Load inv_freq and compute cos/sin for this position\n // Threads cooperate to compute all half_dim cos/sin values\n for (int i = tid; i < half_dim; i += blockDim.x) {\n float angle = (float)n * inv_freq[i];\n cos_vals[i] = cosf(angle);\n sin_vals[i] = sinf(angle);\n }\n __syncthreads();\n \n // Now each thread handles one output element\n // Thread and block mapping: each thread handles a specific (b, h, d)\n // Total work: batch_size * num_heads * dim per position\n \n int total_per_pos = batch_size * num_heads * dim;\n \n for (int idx = tid; idx < total_per_pos; idx += blockDim.x) {\n int d = idx % dim;\n int h = (idx / dim) % num_heads;\n int b = idx / (dim * num_heads);\n \n int base_idx = ((b * num_heads + h) * seq_len + n) * dim;\n \n int freq_idx = (d < half_dim) ? d : (d - half_dim);\n float cos_val = cos_vals[freq_idx];\n float sin_val = sin_vals[freq_idx];\n \n if (d < half_dim) {\n // x1 part: x1 * cos - x2 * sin\n float q_x1 = q[base_idx + d];\n float k_x1 = k[base_idx + d];\n float q_x2 = q[base_idx + d + half_dim];\n float k_x2 = k[base_idx + d + half_dim];\n q_out[base_idx + d] = q_x1 * cos_val - q_x2 * sin_val;\n k_out[base_idx + d] = k_x1 * cos_val - k_x2 * sin_val;\n } else {\n // x2 part: x2 * cos + x1 * sin\n float q_x2 = q[base_idx + d];\n float k_x2 = k[base_idx + d];\n float q_x1 = q[base_idx + d - half_dim];\n float k_x1 = k[base_idx + d - half_dim];\n q_out[base_idx + d] = q_x2 * cos_val + q_x1 * sin_val;\n k_out[base_idx + d] = k_x2 * cos_val + k_x1 * sin_val;\n }\n }\n}\n\ntorch::Tensor apply_rope_cuda(\n torch::Tensor q,\n torch::Tensor k,\n torch::Tensor inv_freq,\n int64_t max_seq_len,\n double base\n) {\n auto q_out = torch::empty_like(q);\n auto k_out = torch::empty_like(k);\n \n int batch_size = q.size(0);\n int num_heads = q.size(1);\n int seq_len = q.size(2);\n int dim = q.size(3);\n int half_dim = dim / 2;\n \n int threads = 256;\n int blocks = seq_len;\n int shared_mem = half_dim * 2 * sizeof(float);\n \n rope_kernel<<<blocks, threads, shared_mem>>>(\n q.data_ptr<float>(),\n k.data_ptr<float>(),\n q_out.data_ptr<float>(),\n k_out.data_ptr<float>(),\n inv_freq.data_ptr<float>(),\n batch_size, num_heads, seq_len, dim\n );\n \n return torch::stack({q_out, k_out}, 0);\n}\n\"\"\"\n\ncpp_source = \"\"\"\ntorch::Tensor apply_rope_cuda(torch::Tensor q, torch::Tensor k, torch::Tensor inv_freq, int64_t max_seq_len, double base);\n\"\"\"\n\nrope_extension = load_inline(\n name='rope_extension_v3',\n cpp_sources=cpp_source,\n cuda_sources=cuda_source,\n functions=['apply_rope_cuda'],\n extra_cuda_cflags=['-O3'],\n verbose=False\n)\n\n\nclass ModelNew(nn.Module):\n \"\"\"\n Rotary Position Embedding (RoPE) applied to protein residue sequences.\n Optimized CUDA implementation.\n \"\"\"\n\n def __init__(self, dim, max_seq_len=2048, base=10000.0):\n super().__init__()\n inv_freq = 1.0 / (base ** (torch.arange(0, dim, 2).float() / dim))\n self.register_buffer(\"inv_freq\", inv_freq)\n self.max_seq_len = max_seq_len\n self.dim = dim\n self.base = base\n\n def forward(self, q: torch.Tensor, k: torch.Tensor) -> tuple:\n \"\"\"\n Args:\n q: (B, H, N, D) \u2013 queries\n k: (B, H, N, D) \u2013 keys\n Returns:\n (q_rot, k_rot) each (B, H, N, D) with RoPE applied\n \"\"\"\n result = rope_extension.apply_rope_cuda(\n q.contiguous(), \n k.contiguous(), \n self.inv_freq,\n self.max_seq_len,\n self.base\n )\n return result[0], result[1]"
}
result ← compile_kernel (ok)
okcompile_kernel
compile_kernel PASSED: kernel compiled without errors.
turn 6 · 1 tool call(s) · 396.8s turn · 11.7s LLM · tokens in=8,778 · out=1,350 · total=10,128 · 6,121 chars (click to expand)
turn 6396.8s turn · 11.7s LLM · 385.0s tools · tokens in=8,778 · out=1,350 · total=10,128
reasoning
Let me check correctness for this shared memory version.
call → run_correctness
callrun_correctness
{
"kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\nimport math\n\ncuda_source = \"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <cmath>\n\n__global__ void rope_kernel(\n const float* __restrict__ q,\n const float* __restrict__ k,\n float* __restrict__ q_out,\n float* __restrict__ k_out,\n const float* __restrict__ inv_freq,\n int batch_size, int num_heads, int seq_len, int dim\n) {\n // Use shared memory for cos/sin values per position\n // Each block handles one (n) position, with threads for different (b, h, d)\n int half_dim = dim / 2;\n \n // Shared memory for cos/sin values for this position\n extern __shared__ float shared_cos_sin[];\n float* cos_vals = shared_cos_sin;\n float* sin_vals = shared_cos_sin + half_dim;\n \n int n = blockIdx.x; // Each block handles one sequence position\n int tid = threadIdx.x;\n \n // Load inv_freq and compute cos/sin for this position\n // Threads cooperate to compute all half_dim cos/sin values\n for (int i = tid; i < half_dim; i += blockDim.x) {\n float angle = (float)n * inv_freq[i];\n cos_vals[i] = cosf(angle);\n sin_vals[i] = sinf(angle);\n }\n __syncthreads();\n \n // Now each thread handles one output element\n // Thread and block mapping: each thread handles a specific (b, h, d)\n // Total work: batch_size * num_heads * dim per position\n \n int total_per_pos = batch_size * num_heads * dim;\n \n for (int idx = tid; idx < total_per_pos; idx += blockDim.x) {\n int d = idx % dim;\n int h = (idx / dim) % num_heads;\n int b = idx / (dim * num_heads);\n \n int base_idx = ((b * num_heads + h) * seq_len + n) * dim;\n \n int freq_idx = (d < half_dim) ? d : (d - half_dim);\n float cos_val = cos_vals[freq_idx];\n float sin_val = sin_vals[freq_idx];\n \n if (d < half_dim) {\n // x1 part: x1 * cos - x2 * sin\n float q_x1 = q[base_idx + d];\n float k_x1 = k[base_idx + d];\n float q_x2 = q[base_idx + d + half_dim];\n float k_x2 = k[base_idx + d + half_dim];\n q_out[base_idx + d] = q_x1 * cos_val - q_x2 * sin_val;\n k_out[base_idx + d] = k_x1 * cos_val - k_x2 * sin_val;\n } else {\n // x2 part: x2 * cos + x1 * sin\n float q_x2 = q[base_idx + d];\n float k_x2 = k[base_idx + d];\n float q_x1 = q[base_idx + d - half_dim];\n float k_x1 = k[base_idx + d - half_dim];\n q_out[base_idx + d] = q_x2 * cos_val + q_x1 * sin_val;\n k_out[base_idx + d] = k_x2 * cos_val + k_x1 * sin_val;\n }\n }\n}\n\ntorch::Tensor apply_rope_cuda(\n torch::Tensor q,\n torch::Tensor k,\n torch::Tensor inv_freq,\n int64_t max_seq_len,\n double base\n) {\n auto q_out = torch::empty_like(q);\n auto k_out = torch::empty_like(k);\n \n int batch_size = q.size(0);\n int num_heads = q.size(1);\n int seq_len = q.size(2);\n int dim = q.size(3);\n int half_dim = dim / 2;\n \n int threads = 256;\n int blocks = seq_len;\n int shared_mem = half_dim * 2 * sizeof(float);\n \n rope_kernel<<<blocks, threads, shared_mem>>>(\n q.data_ptr<float>(),\n k.data_ptr<float>(),\n q_out.data_ptr<float>(),\n k_out.data_ptr<float>(),\n inv_freq.data_ptr<float>(),\n batch_size, num_heads, seq_len, dim\n );\n \n return torch::stack({q_out, k_out}, 0);\n}\n\"\"\"\n\ncpp_source = \"\"\"\ntorch::Tensor apply_rope_cuda(torch::Tensor q, torch::Tensor k, torch::Tensor inv_freq, int64_t max_seq_len, double base);\n\"\"\"\n\nrope_extension = load_inline(\n name='rope_extension_v3',\n cpp_sources=cpp_source,\n cuda_sources=cuda_source,\n functions=['apply_rope_cuda'],\n extra_cuda_cflags=['-O3'],\n verbose=False\n)\n\n\nclass ModelNew(nn.Module):\n \"\"\"\n Rotary Position Embedding (RoPE) applied to protein residue sequences.\n Optimized CUDA implementation.\n \"\"\"\n\n def __init__(self, dim, max_seq_len=2048, base=10000.0):\n super().__init__()\n inv_freq = 1.0 / (base ** (torch.arange(0, dim, 2).float() / dim))\n self.register_buffer(\"inv_freq\", inv_freq)\n self.max_seq_len = max_seq_len\n self.dim = dim\n self.base = base\n\n def forward(self, q: torch.Tensor, k: torch.Tensor) -> tuple:\n \"\"\"\n Args:\n q: (B, H, N, D) \u2013 queries\n k: (B, H, N, D) \u2013 keys\n Returns:\n (q_rot, k_rot) each (B, H, N, D) with RoPE applied\n \"\"\"\n result = rope_extension.apply_rope_cuda(\n q.contiguous(), \n k.contiguous(), \n self.inv_freq,\n self.max_seq_len,\n self.base\n )\n return result[0], result[1]"
}
result ← run_correctness (ok)
okrun_correctness
run_correctness PASSED: (5 / 5) trials all matched the reference.
Numerical precision: max_abs_err=4.77e-07 mean_abs_err=1.43e-08 max_rel_err=8.85e-03
turn 7 · 1 tool call(s) · 330.4s turn · 11.6s LLM · tokens in=10,175 · out=1,338 · total=11,513 · 6,023 chars (click to expand)
turn 7330.4s turn · 11.6s LLM · 318.7s tools · tokens in=10,175 · out=1,338 · total=11,513
reasoning
Good, correctness passes. Now let me try to further optimize using float4 vectorized loads/stores to improve memory throughput. This is key for getting good performance on H100.
assistantLet me try to optimize further using float4 vectorized loads/stores for better memory throughput:
call → compile_kernel
callcompile_kernel
{
"kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\nimport math\n\ncuda_source = \"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <cmath>\n\n__device__ __forceinline__ void sincosf_fast(float x, float* s, float* c) {\n // Use built-in sincosf which is often faster\n sincosf(x, s, c);\n}\n\n__global__ void rope_kernel(\n const float* __restrict__ q,\n const float* __restrict__ k,\n float* __restrict__ q_out,\n float* __restrict__ k_out,\n const float* __restrict__ inv_freq,\n int batch_size, int num_heads, int seq_len, int dim\n) {\n // Each thread handles one (b, h, n, d) element - simple, for good memory coalescing\n // dim is typically 64, so we can have good memory access patterns\n \n int half_dim = dim / 2;\n int total = batch_size * num_heads * seq_len * dim;\n \n for (int idx = blockIdx.x * blockDim.x + threadIdx.x; idx < total; idx += blockDim.x * gridDim.x) {\n int d = idx % dim;\n int n = (idx / dim) % seq_len;\n int h = (idx / (dim * seq_len)) % num_heads;\n int b = idx / (dim * seq_len * num_heads);\n \n int base_idx = ((b * num_heads + h) * seq_len + n) * dim;\n \n // Determine frequency index\n int freq_idx = (d < half_dim) ? d : (d - half_dim);\n \n // Compute angle = n * inv_freq[freq_idx]\n float inv_f = inv_freq[freq_idx];\n float angle = (float)n * inv_f;\n float cos_val, sin_val;\n sincosf_fast(angle, &sin_val, &cos_val);\n \n // Apply rotation\n if (d < half_dim) {\n // x1 part: x1 * cos - x2 * sin\n float q_x1 = q[base_idx + d];\n float k_x1 = k[base_idx + d];\n float q_x2 = q[base_idx + d + half_dim];\n float k_x2 = k[base_idx + d + half_dim];\n q_out[base_idx + d] = q_x1 * cos_val - q_x2 * sin_val;\n k_out[base_idx + d] = k_x1 * cos_val - k_x2 * sin_val;\n } else {\n // x2 part: x2 * cos + x1 * sin\n float q_x2 = q[base_idx + d];\n float k_x2 = k[base_idx + d];\n float q_x1 = q[base_idx + d - half_dim];\n float k_x1 = k[base_idx + d - half_dim];\n q_out[base_idx + d] = q_x2 * cos_val + q_x1 * sin_val;\n k_out[base_idx + d] = k_x2 * cos_val + k_x1 * sin_val;\n }\n }\n}\n\ntorch::Tensor apply_rope_cuda(\n torch::Tensor q,\n torch::Tensor k,\n torch::Tensor inv_freq,\n int64_t max_seq_len,\n double base\n) {\n auto q_out = torch::empty_like(q);\n auto k_out = torch::empty_like(k);\n \n int batch_size = q.size(0);\n int num_heads = q.size(1);\n int seq_len = q.size(2);\n int dim = q.size(3);\n \n int total = batch_size * num_heads * seq_len * dim;\n int threads = 256;\n int blocks = (total + threads - 1) / threads;\n blocks = std::min(blocks, 65535);\n \n rope_kernel<<<blocks, threads>>>(\n q.data_ptr<float>(),\n k.data_ptr<float>(),\n q_out.data_ptr<float>(),\n k_out.data_ptr<float>(),\n inv_freq.data_ptr<float>(),\n batch_size, num_heads, seq_len, dim\n );\n \n return torch::stack({q_out, k_out}, 0);\n}\n\"\"\"\n\ncpp_source = \"\"\"\ntorch::Tensor apply_rope_cuda(torch::Tensor q, torch::Tensor k, torch::Tensor inv_freq, int64_t max_seq_len, double base);\n\"\"\"\n\nrope_extension = load_inline(\n name='rope_extension_v4',\n cpp_sources=cpp_source,\n cuda_sources=cuda_source,\n functions=['apply_rope_cuda'],\n extra_cuda_cflags=['-O3', '--use_fast_math'],\n verbose=False\n)\n\n\nclass ModelNew(nn.Module):\n \"\"\"\n Rotary Position Embedding (RoPE) applied to protein residue sequences.\n Optimized CUDA implementation.\n \"\"\"\n\n def __init__(self, dim, max_seq_len=2048, base=10000.0):\n super().__init__()\n inv_freq = 1.0 / (base ** (torch.arange(0, dim, 2).float() / dim))\n self.register_buffer(\"inv_freq\", inv_freq)\n self.max_seq_len = max_seq_len\n self.dim = dim\n self.base = base\n\n def forward(self, q: torch.Tensor, k: torch.Tensor) -> tuple:\n \"\"\"\n Args:\n q: (B, H, N, D) \u2013 queries\n k: (B, H, N, D) \u2013 keys\n Returns:\n (q_rot, k_rot) each (B, H, N, D) with RoPE applied\n \"\"\"\n result = rope_extension.apply_rope_cuda(\n q.contiguous(), \n k.contiguous(), \n self.inv_freq,\n self.max_seq_len,\n self.base\n )\n return result[0], result[1]"
}
result ← compile_kernel (ok)
okcompile_kernel
compile_kernel PASSED: kernel compiled without errors.
turn 8615.2s turn · 11.0s LLM · 604.2s tools · tokens in=11,490 · out=1,296 · total=12,786
reasoning
Let me run correctness and see if we can improve further.
call → run_correctness
callrun_correctness
{
"kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\nimport math\n\ncuda_source = \"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <cmath>\n\n__device__ __forceinline__ void sincosf_fast(float x, float* s, float* c) {\n // Use built-in sincosf which is often faster\n sincosf(x, s, c);\n}\n\n__global__ void rope_kernel(\n const float* __restrict__ q,\n const float* __restrict__ k,\n float* __restrict__ q_out,\n float* __restrict__ k_out,\n const float* __restrict__ inv_freq,\n int batch_size, int num_heads, int seq_len, int dim\n) {\n // Each thread handles one (b, h, n, d) element - simple, for good memory coalescing\n // dim is typically 64, so we can have good memory access patterns\n \n int half_dim = dim / 2;\n int total = batch_size * num_heads * seq_len * dim;\n \n for (int idx = blockIdx.x * blockDim.x + threadIdx.x; idx < total; idx += blockDim.x * gridDim.x) {\n int d = idx % dim;\n int n = (idx / dim) % seq_len;\n int h = (idx / (dim * seq_len)) % num_heads;\n int b = idx / (dim * seq_len * num_heads);\n \n int base_idx = ((b * num_heads + h) * seq_len + n) * dim;\n \n // Determine frequency index\n int freq_idx = (d < half_dim) ? d : (d - half_dim);\n \n // Compute angle = n * inv_freq[freq_idx]\n float inv_f = inv_freq[freq_idx];\n float angle = (float)n * inv_f;\n float cos_val, sin_val;\n sincosf_fast(angle, &sin_val, &cos_val);\n \n // Apply rotation\n if (d < half_dim) {\n // x1 part: x1 * cos - x2 * sin\n float q_x1 = q[base_idx + d];\n float k_x1 = k[base_idx + d];\n float q_x2 = q[base_idx + d + half_dim];\n float k_x2 = k[base_idx + d + half_dim];\n q_out[base_idx + d] = q_x1 * cos_val - q_x2 * sin_val;\n k_out[base_idx + d] = k_x1 * cos_val - k_x2 * sin_val;\n } else {\n // x2 part: x2 * cos + x1 * sin\n float q_x2 = q[base_idx + d];\n float k_x2 = k[base_idx + d];\n float q_x1 = q[base_idx + d - half_dim];\n float k_x1 = k[base_idx + d - half_dim];\n q_out[base_idx + d] = q_x2 * cos_val + q_x1 * sin_val;\n k_out[base_idx + d] = k_x2 * cos_val + k_x1 * sin_val;\n }\n }\n}\n\ntorch::Tensor apply_rope_cuda(\n torch::Tensor q,\n torch::Tensor k,\n torch::Tensor inv_freq,\n int64_t max_seq_len,\n double base\n) {\n auto q_out = torch::empty_like(q);\n auto k_out = torch::empty_like(k);\n \n int batch_size = q.size(0);\n int num_heads = q.size(1);\n int seq_len = q.size(2);\n int dim = q.size(3);\n \n int total = batch_size * num_heads * seq_len * dim;\n int threads = 256;\n int blocks = (total + threads - 1) / threads;\n blocks = std::min(blocks, 65535);\n \n rope_kernel<<<blocks, threads>>>(\n q.data_ptr<float>(),\n k.data_ptr<float>(),\n q_out.data_ptr<float>(),\n k_out.data_ptr<float>(),\n inv_freq.data_ptr<float>(),\n batch_size, num_heads, seq_len, dim\n );\n \n return torch::stack({q_out, k_out}, 0);\n}\n\"\"\"\n\ncpp_source = \"\"\"\ntorch::Tensor apply_rope_cuda(torch::Tensor q, torch::Tensor k, torch::Tensor inv_freq, int64_t max_seq_len, double base);\n\"\"\"\n\nrope_extension = load_inline(\n name='rope_extension_v4',\n cpp_sources=cpp_source,\n cuda_sources=cuda_source,\n functions=['apply_rope_cuda'],\n extra_cuda_cflags=['-O3', '--use_fast_math'],\n verbose=False\n)\n\n\nclass ModelNew(nn.Module):\n \"\"\"\n Rotary Position Embedding (RoPE) applied to protein residue sequences.\n Optimized CUDA implementation.\n \"\"\"\n\n def __init__(self, dim, max_seq_len=2048, base=10000.0):\n super().__init__()\n inv_freq = 1.0 / (base ** (torch.arange(0, dim, 2).float() / dim))\n self.register_buffer(\"inv_freq\", inv_freq)\n self.max_seq_len = max_seq_len\n self.dim = dim\n self.base = base\n\n def forward(self, q: torch.Tensor, k: torch.Tensor) -> tuple:\n \"\"\"\n Args:\n q: (B, H, N, D) \u2013 queries\n k: (B, H, N, D) \u2013 keys\n Returns:\n (q_rot, k_rot) each (B, H, N, D) with RoPE applied\n \"\"\"\n result = rope_extension.apply_rope_cuda(\n q.contiguous(), \n k.contiguous(), \n self.inv_freq,\n self.max_seq_len,\n self.base\n )\n return result[0], result[1]"
}
result ← run_correctness (ok)
okrun_correctness
run_correctness PASSED: (5 / 5) trials all matched the reference.
Numerical precision: max_abs_err=1.46e-04 mean_abs_err=1.09e-06 max_rel_err=4.60e+00
turn 9170.6s turn · 11.9s LLM · 158.7s tools · tokens in=12,863 · out=1,315 · total=14,178
reasoning
The kernel is correct, but I notice the fast math version has slightly higher numerical error (max_abs_err=1.46e-04 vs 4.77e-07), which is still within tolerance (fp32 uses atol=rtol=1e-4). However, I should consider whether this is acceptable.
Let me think about whether I should submit or try more optimizations. The current kernel:
1. Is correct (passes all tests)
2. Uses fast math for sincos which should be faster
But the error is a bit higher. Let me try a version without fast math but with the sincos optimization, or perhaps I should submit the version that had lower error.
Actually, looking at the error: max_abs_err=1.46e-04 is right at the edge of the tolerance (atol=1e-4). This could potentially fail on some inputs.
Let me try a version that uses sincos (which is still faster than separate sin/cos) but without --use_fast_math to preserve numerical accuracy.
assistantThe fast math version has higher numerical error (1.46e-04 is close to the tolerance limit). Let me try a version that uses sincos optimization but without fast math:
call → compile_kernel
callcompile_kernel
{
"kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\nimport math\n\ncuda_source = \"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <cmath>\n\n__global__ void rope_kernel(\n const float* __restrict__ q,\n const float* __restrict__ k,\n float* __restrict__ q_out,\n float* __restrict__ k_out,\n const float* __restrict__ inv_freq,\n int batch_size, int num_heads, int seq_len, int dim\n) {\n int half_dim = dim / 2;\n int total = batch_size * num_heads * seq_len * dim;\n \n for (int idx = blockIdx.x * blockDim.x + threadIdx.x; idx < total; idx += blockDim.x * gridDim.x) {\n int d = idx % dim;\n int n = (idx / dim) % seq_len;\n int h = (idx / (dim * seq_len)) % num_heads;\n int b = idx / (dim * seq_len * num_heads);\n \n int base_idx = ((b * num_heads + h) * seq_len + n) * dim;\n \n int freq_idx = (d < half_dim) ? d : (d - half_dim);\n \n float inv_f = inv_freq[freq_idx];\n float angle = (float)n * inv_f;\n float cos_val, sin_val;\n sincosf(angle, &sin_val, &cos_val);\n \n if (d < half_dim) {\n float q_x1 = q[base_idx + d];\n float k_x1 = k[base_idx + d];\n float q_x2 = q[base_idx + d + half_dim];\n float k_x2 = k[base_idx + d + half_dim];\n q_out[base_idx + d] = q_x1 * cos_val - q_x2 * sin_val;\n k_out[base_idx + d] = k_x1 * cos_val - k_x2 * sin_val;\n } else {\n float q_x2 = q[base_idx + d];\n float k_x2 = k[base_idx + d];\n float q_x1 = q[base_idx + d - half_dim];\n float k_x1 = k[base_idx + d - half_dim];\n q_out[base_idx + d] = q_x2 * cos_val + q_x1 * sin_val;\n k_out[base_idx + d] = k_x2 * cos_val + k_x1 * sin_val;\n }\n }\n}\n\ntorch::Tensor apply_rope_cuda(\n torch::Tensor q,\n torch::Tensor k,\n torch::Tensor inv_freq,\n int64_t max_seq_len,\n double base\n) {\n auto q_out = torch::empty_like(q);\n auto k_out = torch::empty_like(k);\n \n int batch_size = q.size(0);\n int num_heads = q.size(1);\n int seq_len = q.size(2);\n int dim = q.size(3);\n \n int total = batch_size * num_heads * seq_len * dim;\n int threads = 256;\n int blocks = (total + threads - 1) / threads;\n blocks = std::min(blocks, 65535);\n \n rope_kernel<<<blocks, threads>>>(\n q.data_ptr<float>(),\n k.data_ptr<float>(),\n q_out.data_ptr<float>(),\n k_out.data_ptr<float>(),\n inv_freq.data_ptr<float>(),\n batch_size, num_heads, seq_len, dim\n );\n \n return torch::stack({q_out, k_out}, 0);\n}\n\"\"\"\n\ncpp_source = \"\"\"\ntorch::Tensor apply_rope_cuda(torch::Tensor q, torch::Tensor k, torch::Tensor inv_freq, int64_t max_seq_len, double base);\n\"\"\"\n\nrope_extension = load_inline(\n name='rope_extension_v5',\n cpp_sources=cpp_source,\n cuda_sources=cuda_source,\n functions=['apply_rope_cuda'],\n extra_cuda_cflags=['-O3'],\n verbose=False\n)\n\n\nclass ModelNew(nn.Module):\n \"\"\"\n Rotary Position Embedding (RoPE) applied to protein residue sequences.\n Optimized CUDA implementation.\n \"\"\"\n\n def __init__(self, dim, max_seq_len=2048, base=10000.0):\n super().__init__()\n inv_freq = 1.0 / (base ** (torch.arange(0, dim, 2).float() / dim))\n self.register_buffer(\"inv_freq\", inv_freq)\n self.max_seq_len = max_seq_len\n self.dim = dim\n self.base = base\n\n def forward(self, q: torch.Tensor, k: torch.Tensor) -> tuple:\n result = rope_extension.apply_rope_cuda(\n q.contiguous(), \n k.contiguous(), \n self.inv_freq,\n self.max_seq_len,\n self.base\n )\n return result[0], result[1]"
}
result ← compile_kernel (ok)
okcompile_kernel
compile_kernel PASSED: kernel compiled without errors.
turn 109.9s turn · 9.2s LLM · 0.7s tools · tokens in=14,010 · out=1,086 · total=15,096
reasoning
The kernel is compiled. Let me run a correctness check to ensure it passes, then submit it.
call → run_correctness
callrun_correctness
{
"kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\nimport math\n\ncuda_source = \"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <cmath>\n\n__global__ void rope_kernel(\n const float* __restrict__ q,\n const float* __restrict__ k,\n float* __restrict__ q_out,\n float* __restrict__ k_out,\n const float* __restrict__ inv_freq,\n int batch_size, int num_heads, int seq_len, int dim\n) {\n int half_dim = dim / 2;\n int total = batch_size * num_heads * seq_len * dim;\n \n for (int idx = blockIdx.x * blockDim.x + threadIdx.x; idx < total; idx += blockDim.x * gridDim.x) {\n int d = idx % dim;\n int n = (idx / dim) % seq_len;\n int h = (idx / (dim * seq_len)) % num_heads;\n int b = idx / (dim * seq_len * num_heads);\n \n int base_idx = ((b * num_heads + h) * seq_len + n) * dim;\n \n int freq_idx = (d < half_dim) ? d : (d - half_dim);\n \n float inv_f = inv_freq[freq_idx];\n float angle = (float)n * inv_f;\n float cos_val, sin_val;\n sincosf(angle, &sin_val, &cos_val);\n \n if (d < half_dim) {\n float q_x1 = q[base_idx + d];\n float k_x1 = k[base_idx + d];\n float q_x2 = q[base_idx + d + half_dim];\n float k_x2 = k[base_idx + d + half_dim];\n q_out[base_idx + d] = q_x1 * cos_val - q_x2 * sin_val;\n k_out[base_idx + d] = k_x1 * cos_val - k_x2 * sin_val;\n } else {\n float q_x2 = q[base_idx + d];\n float k_x2 = k[base_idx + d];\n float q_x1 = q[base_idx + d - half_dim];\n float k_x1 = k[base_idx + d - half_dim];\n q_out[base_idx + d] = q_x2 * cos_val + q_x1 * sin_val;\n k_out[base_idx + d] = k_x2 * cos_val + k_x1 * sin_val;\n }\n }\n}\n\ntorch::Tensor apply_rope_cuda(\n torch::Tensor q,\n torch::Tensor k,\n torch::Tensor inv_freq,\n int64_t max_seq_len,\n double base\n) {\n auto q_out = torch::empty_like(q);\n auto k_out = torch::empty_like(k);\n \n int batch_size = q.size(0);\n int num_heads = q.size(1);\n int seq_len = q.size(2);\n int dim = q.size(3);\n \n int total = batch_size * num_heads * seq_len * dim;\n int threads = 256;\n int blocks = (total + threads - 1) / threads;\n blocks = std::min(blocks, 65535);\n \n rope_kernel<<<blocks, threads>>>(\n q.data_ptr<float>(),\n k.data_ptr<float>(),\n q_out.data_ptr<float>(),\n k_out.data_ptr<float>(),\n inv_freq.data_ptr<float>(),\n batch_size, num_heads, seq_len, dim\n );\n \n return torch::stack({q_out, k_out}, 0);\n}\n\"\"\"\n\ncpp_source = \"\"\"\ntorch::Tensor apply_rope_cuda(torch::Tensor q, torch::Tensor k, torch::Tensor inv_freq, int64_t max_seq_len, double base);\n\"\"\"\n\nrope_extension = load_inline(\n name='rope_extension_v5',\n cpp_sources=cpp_source,\n cuda_sources=cuda_source,\n functions=['apply_rope_cuda'],\n extra_cuda_cflags=['-O3'],\n verbose=False\n)\n\n\nclass ModelNew(nn.Module):\n \"\"\"\n Rotary Position Embedding (RoPE) applied to protein residue sequences.\n Optimized CUDA implementation.\n \"\"\"\n\n def __init__(self, dim, max_seq_len=2048, base=10000.0):\n super().__init__()\n inv_freq = 1.0 / (base ** (torch.arange(0, dim, 2).float() / dim))\n self.register_buffer(\"inv_freq\", inv_freq)\n self.max_seq_len = max_seq_len\n self.dim = dim\n self.base = base\n\n def forward(self, q: torch.Tensor, k: torch.Tensor) -> tuple:\n result = rope_extension.apply_rope_cuda(\n q.contiguous(), \n k.contiguous(), \n self.inv_freq,\n self.max_seq_len,\n self.base\n )\n return result[0], result[1]"
}
result ← run_correctness (ok)
okrun_correctness
run_correctness PASSED: (5 / 5) trials all matched the reference.
Numerical precision: max_abs_err=4.77e-07 mean_abs_err=1.43e-08 max_rel_err=8.85e-03
turn 1165.4s turn · 9.3s LLM · 56.2s tools · tokens in=15,222 · out=1,104 · total=16,326 · FINAL
reasoning
The kernel is correct with good numerical precision (max_abs_err=4.77e-07, well within the 1e-4 tolerance for fp32). Let me submit this version.
call → submit_kernel
callsubmit_kernel
{
"kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\nimport math\n\ncuda_source = \"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <cmath>\n\n__global__ void rope_kernel(\n const float* __restrict__ q,\n const float* __restrict__ k,\n float* __restrict__ q_out,\n float* __restrict__ k_out,\n const float* __restrict__ inv_freq,\n int batch_size, int num_heads, int seq_len, int dim\n) {\n int half_dim = dim / 2;\n int total = batch_size * num_heads * seq_len * dim;\n \n for (int idx = blockIdx.x * blockDim.x + threadIdx.x; idx < total; idx += blockDim.x * gridDim.x) {\n int d = idx % dim;\n int n = (idx / dim) % seq_len;\n int h = (idx / (dim * seq_len)) % num_heads;\n int b = idx / (dim * seq_len * num_heads);\n \n int base_idx = ((b * num_heads + h) * seq_len + n) * dim;\n \n int freq_idx = (d < half_dim) ? d : (d - half_dim);\n \n float inv_f = inv_freq[freq_idx];\n float angle = (float)n * inv_f;\n float cos_val, sin_val;\n sincosf(angle, &sin_val, &cos_val);\n \n if (d < half_dim) {\n float q_x1 = q[base_idx + d];\n float k_x1 = k[base_idx + d];\n float q_x2 = q[base_idx + d + half_dim];\n float k_x2 = k[base_idx + d + half_dim];\n q_out[base_idx + d] = q_x1 * cos_val - q_x2 * sin_val;\n k_out[base_idx + d] = k_x1 * cos_val - k_x2 * sin_val;\n } else {\n float q_x2 = q[base_idx + d];\n float k_x2 = k[base_idx + d];\n float q_x1 = q[base_idx + d - half_dim];\n float k_x1 = k[base_idx + d - half_dim];\n q_out[base_idx + d] = q_x2 * cos_val + q_x1 * sin_val;\n k_out[base_idx + d] = k_x2 * cos_val + k_x1 * sin_val;\n }\n }\n}\n\ntorch::Tensor apply_rope_cuda(\n torch::Tensor q,\n torch::Tensor k,\n torch::Tensor inv_freq,\n int64_t max_seq_len,\n double base\n) {\n auto q_out = torch::empty_like(q);\n auto k_out = torch::empty_like(k);\n \n int batch_size = q.size(0);\n int num_heads = q.size(1);\n int seq_len = q.size(2);\n int dim = q.size(3);\n \n int total = batch_size * num_heads * seq_len * dim;\n int threads = 256;\n int blocks = (total + threads - 1) / threads;\n blocks = std::min(blocks, 65535);\n \n rope_kernel<<<blocks, threads>>>(\n q.data_ptr<float>(),\n k.data_ptr<float>(),\n q_out.data_ptr<float>(),\n k_out.data_ptr<float>(),\n inv_freq.data_ptr<float>(),\n batch_size, num_heads, seq_len, dim\n );\n \n return torch::stack({q_out, k_out}, 0);\n}\n\"\"\"\n\ncpp_source = \"\"\"\ntorch::Tensor apply_rope_cuda(torch::Tensor q, torch::Tensor k, torch::Tensor inv_freq, int64_t max_seq_len, double base);\n\"\"\"\n\nrope_extension = load_inline(\n name='rope_extension_v5',\n cpp_sources=cpp_source,\n cuda_sources=cuda_source,\n functions=['apply_rope_cuda'],\n extra_cuda_cflags=['-O3'],\n verbose=False\n)\n\n\nclass ModelNew(nn.Module):\n \"\"\"\n Rotary Position Embedding (RoPE) applied to protein residue sequences.\n Optimized CUDA implementation.\n \"\"\"\n\n def __init__(self, dim, max_seq_len=2048, base=10000.0):\n super().__init__()\n inv_freq = 1.0 / (base ** (torch.arange(0, dim, 2).float() / dim))\n self.register_buffer(\"inv_freq\", inv_freq)\n self.max_seq_len = max_seq_len\n self.dim = dim\n self.base = base\n\n def forward(self, q: torch.Tensor, k: torch.Tensor) -> tuple:\n result = rope_extension.apply_rope_cuda(\n q.contiguous(), \n k.contiguous(), \n self.inv_freq,\n self.max_seq_len,\n self.base\n )\n return result[0], result[1]"
}
result ← submit_kernel (ok)
oksubmit_kernel
submit_kernel PASSED: (5 / 5) correctness trials all passed.
Kernel runtime: 0.02 μs
Runtime stats: mean=0.02μs median=?μs std=0.00μs
Numerical precision: max_abs_err=4.77e-07 mean_abs_err=1.43e-08 max_rel_err=8.85e-03
Memory: 45.0 MB peak (ref: 45.13 MB, ratio: 0.9972x)
Kernel launches: 11 (ref: 45, fusion ratio: 4.0909)
SOL score: 0.384
final submitted kernel (125 lines, 3,791 chars) — full source, untruncated
import torch
import torch.nn as nn
from torch.utils.cpp_extension import load_inline
import math
cuda_source = """
#include <torch/extension.h>
#include <cuda.h>
#include <cuda_runtime.h>
#include <cmath>
__global__ void rope_kernel(
const float* __restrict__ q,
const float* __restrict__ k,
float* __restrict__ q_out,
float* __restrict__ k_out,
const float* __restrict__ inv_freq,
int batch_size, int num_heads, int seq_len, int dim
) {
int half_dim = dim / 2;
int total = batch_size * num_heads * seq_len * dim;
for (int idx = blockIdx.x * blockDim.x + threadIdx.x; idx < total; idx += blockDim.x * gridDim.x) {
int d = idx % dim;
int n = (idx / dim) % seq_len;
int h = (idx / (dim * seq_len)) % num_heads;
int b = idx / (dim * seq_len * num_heads);
int base_idx = ((b * num_heads + h) * seq_len + n) * dim;
int freq_idx = (d < half_dim) ? d : (d - half_dim);
float inv_f = inv_freq[freq_idx];
float angle = (float)n * inv_f;
float cos_val, sin_val;
sincosf(angle, &sin_val, &cos_val);
if (d < half_dim) {
float q_x1 = q[base_idx + d];
float k_x1 = k[base_idx + d];
float q_x2 = q[base_idx + d + half_dim];
float k_x2 = k[base_idx + d + half_dim];
q_out[base_idx + d] = q_x1 * cos_val - q_x2 * sin_val;
k_out[base_idx + d] = k_x1 * cos_val - k_x2 * sin_val;
} else {
float q_x2 = q[base_idx + d];
float k_x2 = k[base_idx + d];
float q_x1 = q[base_idx + d - half_dim];
float k_x1 = k[base_idx + d - half_dim];
q_out[base_idx + d] = q_x2 * cos_val + q_x1 * sin_val;
k_out[base_idx + d] = k_x2 * cos_val + k_x1 * sin_val;
}
}
}
torch::Tensor apply_rope_cuda(
torch::Tensor q,
torch::Tensor k,
torch::Tensor inv_freq,
int64_t max_seq_len,
double base
) {
auto q_out = torch::empty_like(q);
auto k_out = torch::empty_like(k);
int batch_size = q.size(0);
int num_heads = q.size(1);
int seq_len = q.size(2);
int dim = q.size(3);
int total = batch_size * num_heads * seq_len * dim;
int threads = 256;
int blocks = (total + threads - 1) / threads;
blocks = std::min(blocks, 65535);
rope_kernel<<<blocks, threads>>>(
q.data_ptr<float>(),
k.data_ptr<float>(),
q_out.data_ptr<float>(),
k_out.data_ptr<float>(),
inv_freq.data_ptr<float>(),
batch_size, num_heads, seq_len, dim
);
return torch::stack({q_out, k_out}, 0);
}
"""
cpp_source = """
torch::Tensor apply_rope_cuda(torch::Tensor q, torch::Tensor k, torch::Tensor inv_freq, int64_t max_seq_len, double base);
"""
rope_extension = load_inline(
name='rope_extension_v5',
cpp_sources=cpp_source,
cuda_sources=cuda_source,
functions=['apply_rope_cuda'],
extra_cuda_cflags=['-O3'],
verbose=False
)
class ModelNew(nn.Module):
"""
Rotary Position Embedding (RoPE) applied to protein residue sequences.
Optimized CUDA implementation.
"""
def __init__(self, dim, max_seq_len=2048, base=10000.0):
super().__init__()
inv_freq = 1.0 / (base ** (torch.arange(0, dim, 2).float() / dim))
self.register_buffer("inv_freq", inv_freq)
self.max_seq_len = max_seq_len
self.dim = dim
self.base = base
def forward(self, q: torch.Tensor, k: torch.Tensor) -> tuple:
result = rope_extension.apply_rope_cuda(
q.contiguous(),
k.contiguous(),
self.inv_freq,
self.max_seq_len,
self.base
)
return result[0], result[1]