FW-GLM-5-1 · popcorn · pop_l123_st_gpt
compile_fail 29_logsumexp_across_virtual_ranks.py
model
FW-GLM-5-1
turns / max
1 / 1
tool calls
1
tokens (total)
2943
wall clock
199.4s
truncated
no
backend
cuda fp32
variant
popcorn
runtime
—
ref runtime
-1.00 μs
speedup
—
started
2026-05-11T09:18:09Z
compile error: load_inline() got an unexpected keyword argument 'cuda_src'
timingkernelbench.timing
kernel runtime
—
ref runtime
—
speedup
—
kernel mean / median / std
—
ref mean / median / std
—
reference PyTorch source (the prompt the agent sees)
"""
Stable log-sum-exp over virtual ranks: out = logsumexp(x, dim=0), shape (B, D).
"""

import torch
import torch.nn as nn

from kernelbench.distributed_collectives import default_device

R = 4
B = 10
D = 32


class Model(nn.Module):
    def __init__(self):
        super().__init__()

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        return torch.logsumexp(x, dim=0)


def get_inputs():
    dev = default_device()
    p = popcorn_pri
    return [torch.randn(p.jitter_int(R), p.jitter_int(B), p.jitter_int(D, align=8), device=dev)]


def get_init_inputs():
    return []
turn 0 · FINAL · 1 tool call(s) · 199.4s turn · 24.5s LLM · tokens in=976 · out=1,967 · total=2,943 · 9,035 chars (click to expand)
turn 0199.4s turn · 24.5s LLM · 174.9s tools · tokens in=976 · out=1,967 · total=2,943 · FINAL
reasoning
Let me analyze the task: 1. I need to implement a custom CUDA kernel for `torch.logsumexp(x, dim=0)` where x has shape (R, B, D) 2. The output should have shape (B, D) 3. I need to compute logsumexp over the first dimension (R=4 elements for each (B, D) position) The logsumexp formula is: logsumexp(x) = log(sum(exp(x))) For numerical stability, we use: logsumexp(x) = max(x) + log(sum(exp(x - max(x)))) This is a reduction operation over dimension 0. Let me think about how to optimize this: 1. The input is (R=4, B=10, D=32), which is quite small 2. We're reducing over R=4 elements for each of B*D=10*32=320 positions 3. This is a small problem size, so we want minimal kernel launch overhead For a CUDA kernel: - Each thread could handle one (B, D) position - Each thread loads R values, computes max, then computes logsumexp - Total threads needed: B * D = 320 Since R=4 is small, each thread can easily handle this without shared memory. Let me write the kernel: ```python import torch import torch.nn as nn from torch.utils.cpp_extension import load_inline # Compile the CUDA kernel cuda_source = """ #include <torch/extension.h> #include <cuda.h> #include <cuda_runtime.h> #include <cmath> __global__ void logsumexp_kernel(const float* __restrict__ x, float* __restrict__ out, int R, int B, int D) { int bd_idx = blockIdx.x * blockDim.x + threadIdx.x; int total = B * D; if (bd_idx >= total) return; int b = bd_idx / D; int d = bd_idx % D; // Find max value over R dimension float max_val = x[0 * B * D + b * D + d]; for (int r = 1; r < R; r++) { float val = x[r * B * D + b * D + d]; if (val > max_val) max_val = val; } // Compute sum of exp(x - max) float sum_exp = 0.0f; for (int r = 0; r < R; r++) { float val = x[r * B * D + b * D + d]; sum_exp += expf(val - max_val); } // Compute logsumexp out[b * D + d] = max_val + logf(sum_exp); } ``` The kernel iterates through each position in the B×D grid, finds the maximum value across R elements, computes the stable logsumexp by subtracting the max before exponentiating, and stores the result. This approach minimizes kernel overhead for small problem sizes. The host wrapper function validates input dimensions, allocates output tensor, launches with 256 threads per block scaling appropriately for the total elements, and synchronizes to ensure completion before returning.ool>& vec) { vec.clear(); vec.emplace_back(torch::.randn({R, B, D}, torch::device(torch::kCUDA).generator(g))); } std::vector<torch::Tensor> get_init_inputs() { return {}; } """) def get_inputs(): return get_inputs_cpp() def get_init_inputs(): return get_init_inputs_cpp() class ModelNew(nn.Module): def __init__(self): super().__init__() self.logsumext_cuda = logsumext_cuda def forward(self, x: torch.Tensor) -> torch.Tensor: return self.logsumext_cuda.logsumexp_cuda(x) ``` I'm realizing the device initialization approach won't work properly here. I need to use the actual device from `default_device()` and create tensors on CUDA directly, but I can't import Python modules from C++. Let me reconsider the tensor creation strategy to handle device placement correctly. Actually, looking at the original reference code, those utility functions (`get_inputs` and `get_init_inputs`) aren't part of the model itself—they're just for testing. The `ModelNew` class only needs to implement `__init__` and `forward` with matching signatures. I should focus on getting the core model logic right rather than those helper functions. For the CUDA kernel, I need to implement logsumexp efficiently. The kernel iterates over the B×D output space, finding the maximum value across the R dimension first, then computing the stable log-sum-exp formula by subtracting this max before exponentiating. This prevents numerical overflow when dealing with large values in the input tensor. The host function validates that the input is contiguous and CUDA-allocated, extracts the dimensions, allocates the output tensor, and launches the kernel with enough threads to cover all B×D positions using 256 threads per block. Then the module defines the CUDA extension with the kernel source and exposes the `logsumexp_cuda` function, while providing helper functions to generate random input tensors with shape (R, B, D) on CUDA and return an empty initialization list. I need to reconsider the indexing strategy. The input tensor has shape (R, B, D) with stride [B*D, D, 1], so for element x[r, b, d], the linear index should be r*B*D + b*D + d. My kernel implementation appears correct in this regard. I should verify the thread calculation works properly with B=10 and D=32, which gives 320 total elements that fit well within the 256-thread block configuration. Looking at the original reference code, I notice the `get_inputs()` and `get_init_inputs()` functions aren't part of the Model class itself—they're separate helper functions. Since the task specifies that ModelNew must accept the same constructor arguments and implement forward() with the same signature, I should focus on matching the Model interface rather than reproducing those utility functions. The Model class has a simple `__init__` with no arguments, so ModelNew should follow the same pattern. For the CUDA kernel optimization, since R=4, B=10, D=32, the kernel handles 320 threads total, which is well-suited for a single warp approach where each thread processes one element independently. The main performance considerations are ensuring coalesced memory reads across adjacent threads (which works well with contiguous B*D layout), minimizing kernel launch overhead for this small problem size, and using fast math functions like __expf and __logf for better throughput. For float precision, the standard math functions provide sufficient accuracy, though there's a trade-off between speed and precision that could be explored with fast math intrinsics if needed.
call → submit_kernel
callsubmit_kernel { "kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\nlogsumext_cuda = load_inline(\n name='logsumext_cuda',\n cuda_src=\"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <cmath>\n\n__global__ void logsumexp_kernel(const float* __restrict__ x, float* __restrict__ out, \n int R, int BD) {\n int bd_idx = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (bd_idx >= BD) return;\n \n // Process R elements for this (b, d) position\n // Input layout: [R][B][D] contiguous, so x[r][b][d] at offset r*BD + bd_idx\n \n // Find max for numerical stability\n float max_val = x[bd_idx]; // r=0\n for (int r = 1; r < R; r++) {\n float val = x[r * BD + bd_idx];\n if (val > max_val) max_val = val;\n }\n \n // Compute logsumexp: log(sum(exp(x))) = max + log(sum(exp(x - max)))\n float sum_exp = 0.0f;\n for (int r = 0; r < R; r++) {\n float val = x[r * BD + bd_idx];\n sum_exp += expf(val - max_val);\n }\n \n out[bd_idx] = max_val + logf(sum_exp);\n}\n\ntorch::Tensor logsumexp_cuda(torch::Tensor x) {\n // x: [R, B, D], output: [B, D]\n int R = x.size(0);\n int B = x.size(1);\n int D = x.size(2);\n int BD = B * D;\n \n auto out = torch::empty({B, D}, x.options());\n \n const int threads = 256;\n const int blocks = (BD + threads - 1) / threads;\n \n logsumexp_kernel<<<blocks, threads>>>(\n x.data_ptr<float>(),\n out.data_ptr<float>(),\n R, BD\n );\n \n return out;\n}\n\"\"\",\n functions=['logsumexp_cuda'],\n extra_cuda_cflags=['-O3'],\n)\n\n\nclass ModelNew(nn.Module):\n def __init__(self):\n super().__init__()\n\n def forward(self, x: torch.Tensor) -> torch.Tensor:\n return logsumext_cuda.logsumexp_cuda(x)" }
result ← submit_kernel (fail)
failsubmit_kernel submit_kernel FAILED: kernel did not compile. load_inline() got an unexpected keyword argument 'cuda_src'
final submitted kernel (70 lines, 1,823 chars) — full source, untruncated
import torch
import torch.nn as nn
from torch.utils.cpp_extension import load_inline

logsumext_cuda = load_inline(
    name='logsumext_cuda',
    cuda_src="""
#include <torch/extension.h>
#include <cuda.h>
#include <cuda_runtime.h>
#include <cmath>

__global__ void logsumexp_kernel(const float* __restrict__ x, float* __restrict__ out, 
                                  int R, int BD) {
    int bd_idx = blockIdx.x * blockDim.x + threadIdx.x;
    
    if (bd_idx >= BD) return;
    
    // Process R elements for this (b, d) position
    // Input layout: [R][B][D] contiguous, so x[r][b][d] at offset r*BD + bd_idx
    
    // Find max for numerical stability
    float max_val = x[bd_idx];  // r=0
    for (int r = 1; r < R; r++) {
        float val = x[r * BD + bd_idx];
        if (val > max_val) max_val = val;
    }
    
    // Compute logsumexp: log(sum(exp(x))) = max + log(sum(exp(x - max)))
    float sum_exp = 0.0f;
    for (int r = 0; r < R; r++) {
        float val = x[r * BD + bd_idx];
        sum_exp += expf(val - max_val);
    }
    
    out[bd_idx] = max_val + logf(sum_exp);
}

torch::Tensor logsumexp_cuda(torch::Tensor x) {
    // x: [R, B, D], output: [B, D]
    int R = x.size(0);
    int B = x.size(1);
    int D = x.size(2);
    int BD = B * D;
    
    auto out = torch::empty({B, D}, x.options());
    
    const int threads = 256;
    const int blocks = (BD + threads - 1) / threads;
    
    logsumexp_kernel<<<blocks, threads>>>(
        x.data_ptr<float>(),
        out.data_ptr<float>(),
        R, BD
    );
    
    return out;
}
""",
    functions=['logsumexp_cuda'],
    extra_cuda_cflags=['-O3'],
)


class ModelNew(nn.Module):
    def __init__(self):
        super().__init__()

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        return logsumext_cuda.logsumexp_cuda(x)