grok-4.3 · popcorn · pop_l123_st_gpt
correct 9_AssociativeScan.py
model
grok-4.3
turns / max
1 / 1
tool calls
1
tokens (total)
2688
wall clock
246.7s
truncated
no
backend
cuda fp32
variant
popcorn
runtime
0.85 μs
ref runtime
88.10 μs
speedup
103.16x
started
2026-05-11T08:49:00Z
timingkernelbench.timing
kernel runtime
0.85 μs
ref runtime
88.10 μs
speedup
103.16x
kernel mean / median / std
0.85 / — / 0.00 μs
ref mean / median / std
88.10 / — / 2.02 μs
numerical precisioneval_kernel_against_ref
max abs error
9.54e-07
mean abs error
2.04e-08
max rel error
1.75
mean rel error
2.02e-07
gpu memoryextended_metrics.measure_memory
kernel peak
384.00 MB
ref peak
512.00 MB
kernel / ref
0.75x
kernel launches / fusiontorch.profiler
kernel launches
1
ref launches
24583
fusion ratio (ref / kernel)
24583.00x
total cuda time
34 μs
SOL (speed-of-light)nsight_hardware_counters
SOL score (0–1)
0.139
dram util %
13.8%
compute util %
0.0%
bottleneck
unknown
dominant pipe
None
arithmetic intensity
—
ridge point
20.00
roofline / occupancynsight_hardware_counters
dram bandwidth
464.00 GB/s
dram util %
13.9%
fp32 tflops
—
fp32 util %
—
fp16 tflops
—
occupancy %
12.4%
bottleneck
unknown
dominant pipe %
—
arithmetic intensity
—
ridge point
20.00
L1 hit %
—
L2 hit %
33.5%
ld sectors / req
4.00
st sectors / req
—
regs / thread
40
smem / block
1024 B
block size
256
peak bw
3350.00 GB/s
peak fp32 tflops
67.00
peak fp16 tflops
1979.00
warp stalls (top 5)nsight
long_scoreboard
4964.6%
short_scoreboard
0.3%
flagssubmit_kernel guard
excessive speedup
flagged
reference PyTorch source (the prompt the agent sees)
import torch
import torch.nn as nn

class Model(nn.Module):
    """
    Performs a parallel associative scan (linear recurrence) over a 1D sequence.
    Given coefficient a and input b at each timestep, computes:
        h_0 = b_0
        h_t = a_t * h_{t-1} + b_t  for t > 0
    This is the core parallelizable primitive underlying Mamba's selective scan and
    other SSM architectures. Sequential in naive PyTorch but can be parallelized
    via the associative scan algorithm in O(L log L) span.

    Args:
        d_model (int): Number of independent recurrence channels.
    """
    def __init__(self, d_model: int):
        super(Model, self).__init__()
        self.d_model = d_model

    def forward(self, a: torch.Tensor, b: torch.Tensor) -> torch.Tensor:
        B, L, D = a.shape
        h = torch.zeros(B, D, device=a.device, dtype=a.dtype)
        outputs = []
        for t in range(L):
            h = a[:, t] * h + b[:, t]
            outputs.append(h)
        return torch.stack(outputs, dim=1)

batch_size = 32
d_model = 256
seq_len = 4096

def get_inputs():
    a = torch.sigmoid(torch.randn(popcorn_pri.jitter_int(batch_size), popcorn_pri.jitter_int(seq_len), d_model))
    b = torch.randn(popcorn_pri.jitter_int(batch_size), popcorn_pri.jitter_int(seq_len), d_model)
    return [a, b]

def get_init_inputs():
    return [d_model]
turn 0246.7s turn · 22.4s LLM · 224.3s tools · tokens in=1,195 · out=468 · total=2,688 · FINAL
reasoning
The task is to replace the forward computation of a PyTorch model with a custom CUDA kernel for a parallel associative scan, specifically a linear recurrence: h_t = a_t * h_{t-1} + b_t. I need to replace the forward computation of a PyTorch model with a custom CUDA kernel for a parallel associative scan, specifically a linear recurrence: h_t = a_t * h_{t seq_len=4096. The sequential loop in PyTorch is slow for long sequences, so I'm implementing a fast CUDA kernel using parallel parallel associative scan. I'll use a simple parallelization: launch 8192 threads, each handling one independent sequence of length 4096, performing the recurrence sequentially per thread. This is efficient because there are 32 batches * 256 dims = 8192 independent sequences, and each thread processes one, performing ~409 6 floating point operations, totaling manageable computation. For implementation, I'll define a CUDA kernel that takes pointers to a, b, and output arrays, along with shapes B, L, D.
call → submit_kernel
callsubmit_kernel { "kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\ncuda_source = '''\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n\n__global__ void parallel_scan_kernel(const float* __restrict__ a,\n const float* __restrict__ b,\n float* __restrict__ out,\n const int B, const int L, const int D) {\n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n if (idx >= B * D) return;\n int batch = idx / D;\n int dim = idx % D;\n\n float h = 0.0f;\n for (int t = 0; t < L; t++) {\n int offset = (batch * L + t) * D + dim;\n float aa = a[offset];\n float bb = b[offset];\n h = aa * h + bb;\n out[offset] = h;\n }\n}\n\ntorch::Tensor parallel_scan_cuda(torch::Tensor a, torch::Tensor b) {\n auto B = a.size(0);\n auto L = a.size(1);\n auto D = a.size(2);\n auto out = torch::empty({B, L, D}, a.options());\n int total = B * D;\n int threads = 256;\n int blocks = (total + threads - 1) / threads;\n parallel_scan_kernel<<<blocks, threads>>>(\n a.data_ptr<float>(), b.data_ptr<float>(), out.data_ptr<float>(),\n B, L, D);\n return out;\n}\n'''\n\ncpp_source = '''\ntorch::Tensor parallel_scan_cuda(torch::Tensor a, torch::Tensor b);\n'''\n\nscan_module = load_inline(\n name='parallel_scan',\n cpp_sources=[cpp_source],\n cuda_sources=[cuda_source],\n functions=['parallel_scan_cuda'],\n verbose=False\n)\n\nclass ModelNew(nn.Module):\n def __init__(self, d_model: int):\n super(ModelNew, self).__init__()\n self.d_model = d_model\n\n def forward(self, a: torch.Tensor, b: torch.Tensor) -> torch.Tensor:\n return scan_module.parallel_scan_cuda(a, b)\n" }
result ← submit_kernel (ok)
oksubmit_kernel submit_kernel PASSED: (5 / 5) correctness trials all passed. Kernel runtime: 0.85 μs Runtime stats: mean=0.85μs median=?μs std=0.00μs Numerical precision: max_abs_err=9.54e-07 mean_abs_err=2.04e-08 max_rel_err=1.75e+00 Memory: 384.0 MB peak (ref: 512.0 MB, ratio: 0.75x) Kernel launches: 1 (ref: 24583, fusion ratio: 24583.0) SOL score: 0.1385 Flagged for excessive speedup — this submission may have been rejected by automated review.
final submitted kernel (63 lines, 1,784 chars) — full source, untruncated
import torch
import torch.nn as nn
from torch.utils.cpp_extension import load_inline

cuda_source = '''
#include <torch/extension.h>
#include <cuda.h>
#include <cuda_runtime.h>

__global__ void parallel_scan_kernel(const float* __restrict__ a,
                                      const float* __restrict__ b,
                                      float* __restrict__ out,
                                      const int B, const int L, const int D) {
    int idx = blockIdx.x * blockDim.x + threadIdx.x;
    if (idx >= B * D) return;
    int batch = idx / D;
    int dim = idx % D;

    float h = 0.0f;
    for (int t = 0; t < L; t++) {
        int offset = (batch * L + t) * D + dim;
        float aa = a[offset];
        float bb = b[offset];
        h = aa * h + bb;
        out[offset] = h;
    }
}

torch::Tensor parallel_scan_cuda(torch::Tensor a, torch::Tensor b) {
    auto B = a.size(0);
    auto L = a.size(1);
    auto D = a.size(2);
    auto out = torch::empty({B, L, D}, a.options());
    int total = B * D;
    int threads = 256;
    int blocks = (total + threads - 1) / threads;
    parallel_scan_kernel<<<blocks, threads>>>(
        a.data_ptr<float>(), b.data_ptr<float>(), out.data_ptr<float>(),
        B, L, D);
    return out;
}
'''

cpp_source = '''
torch::Tensor parallel_scan_cuda(torch::Tensor a, torch::Tensor b);
'''

scan_module = load_inline(
    name='parallel_scan',
    cpp_sources=[cpp_source],
    cuda_sources=[cuda_source],
    functions=['parallel_scan_cuda'],
    verbose=False
)

class ModelNew(nn.Module):
    def __init__(self, d_model: int):
        super(ModelNew, self).__init__()
        self.d_model = d_model

    def forward(self, a: torch.Tensor, b: torch.Tensor) -> torch.Tensor:
        return scan_module.parallel_scan_cuda(a, b)