gpt-5.5-priority · popcorn · l123_single_gpt
incorrect 10_SampledDenseDenseMatmulEdges.py
model
gpt-5.5-priority
turns / max
1 / 1
tool calls
1
tokens (total)
0
wall clock
—
truncated
no
backend
cuda fp32
variant
popcorn
runtime
—
ref runtime
0.05 μs
speedup
—
started
2026-05-05T01:12:27Z
correctness issue:
timingkernelbench.timing
kernel runtime
—
ref runtime
0.05 μs
speedup
—
kernel mean / median / std
—
ref mean / median / std
0.05 / — / 6.54e-04 μs
roofline / occupancynsight_hardware_counters
dram bandwidth
—
dram util %
—
fp32 tflops
—
fp32 util %
—
fp16 tflops
—
occupancy %
—
bottleneck
unknown
dominant pipe %
—
arithmetic intensity
—
ridge point
20.00
L1 hit %
—
L2 hit %
—
ld sectors / req
—
st sectors / req
—
regs / thread
—
smem / block
—
block size
—
peak bw
3350.00 GB/s
peak fp32 tflops
67.00
peak fp16 tflops
1979.00
reference PyTorch source (the prompt the agent sees)
import torch
import torch.nn as nn


class Model(nn.Module):
    """
    Edge-only sampled dense-dense matmul.

    For each graph edge (src, dst), compute a dot product between source and
    destination node embeddings. This pattern appears in link prediction,
    sampled attention, and graph contrastive learning.
    """

    def __init__(self):
        super().__init__()

    def forward(
        self,
        src_idx: torch.Tensor,
        dst_idx: torch.Tensor,
        src_feat: torch.Tensor,
        dst_feat: torch.Tensor,
    ) -> torch.Tensor:
        src = src_feat[src_idx.long()]
        dst = dst_feat[dst_idx.long()]
        return (src * dst).sum(dim=-1)


num_src_nodes = 1024
num_dst_nodes = 768
num_edges = 16384
feat_dim = 128


def get_inputs():
    p = popcorn_pri
    n_edges = p.jitter_int(num_edges)
    fd = p.jitter_int(feat_dim, align=8)
    ns = p.jitter_int(num_src_nodes)
    nd = p.jitter_int(num_dst_nodes)
    src_idx = torch.randint(0, ns, (n_edges,), dtype=torch.int32)
    dst_idx = torch.randint(0, nd, (n_edges,), dtype=torch.int32)
    src_feat = torch.randn(ns, fd, dtype=torch.float32)
    dst_feat = torch.randn(nd, fd, dtype=torch.float32)
    return [src_idx, dst_idx, src_feat, dst_feat]





def get_init_inputs():
    return []
turn 0 · FINAL · 1 tool call(s) · 55.1s turn · 55.1s LLM · 9,062 chars (click to expand)
turn 055.1s turn · 55.1s LLM · 0.0s tools · FINAL
reasoning
**Optimizing CUDA Code** I need to submit the final code and use a custom CUDA setup for specified parameters, looking at E=16384 and D=128. I'll have to load the model inline and optimize the kernel for dot products. Input types will be int32 indices, while outputs will be in float32, and I'm aiming for performance faster than PyTorch's methods. The design involves significant optimization with memory loads coalesced and potentially using a warp per edge, reducing the block count. **Supporting CUDA Tensors** I need to ensure support for CUDA tensors and compile everything correctly. There’s a potential issue with input indices; if they’re int32 and PyTorch uses int64, I'll need to manage that. The forward signature could handle only int32 or a dispatch system could be considered. I will include necessary imports in a Python file and set up loading inline with C++ sources for the host wrapper. It’s crucial to declare functions in the C++ sources to avoid linking issues, ensuring they’re visible in the current translation unit. **Using Torch with CUDA** I could use `torch.empty_like` for the indices, which should streamline memory allocation. For the CUDA launch, I need to ensure I'm including `torch/extension.h` in my C++ files. It seems like loading inline might also handle the necessary includes, so I'll make sure to add those correctly. Overall, this setup should help optimize how I'm structuring the code. I want to ensure everything is set up efficiently for better performance. **Evaluating PyTorch types** I'm looking at how scalar types in PyTorch work. The int32 scalar type is represented as `at::kInt`, whereas int64 is `at::kLong`. I notice specific checks in the code, like confirming that `src_feat` is a float using `TORCH_CHECK`, and that both source and destination features are contiguous with another `TORCH_CHECK`. It's important to evaluate these conditions correctly to ensure everything operates smoothly. Understanding these checks is crucial for maintaining data integrity in my PyTorch implementation!
call → submit_kernel
callsubmit_kernel { "kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\n_cpp_src = r'''\n#include <torch/extension.h>\n\ntorch::Tensor edge_dot_forward(torch::Tensor src_idx,\n torch::Tensor dst_idx,\n torch::Tensor src_feat,\n torch::Tensor dst_feat);\n'''\n\n_cuda_src = r'''\n#include <torch/extension.h>\n#include <ATen/cuda/CUDAContext.h>\n#include <c10/cuda/CUDAException.h>\n#include <cstdint>\n\ntemplate <typename index_t>\n__global__ __launch_bounds__(256, 2)\nvoid edge_dot128_kernel(const index_t* __restrict__ src_idx,\n const index_t* __restrict__ dst_idx,\n const float* __restrict__ src_feat,\n const float* __restrict__ dst_feat,\n float* __restrict__ out,\n int E) {\n constexpr int WARPS_PER_BLOCK = 8;\n int tid = threadIdx.x;\n int lane = tid & 31;\n int warp = tid >> 5;\n int edge = blockIdx.x * WARPS_PER_BLOCK + warp;\n if (edge >= E) return;\n\n int s = static_cast<int>(src_idx[edge]);\n int d = static_cast<int>(dst_idx[edge]);\n const float* __restrict__ sp = src_feat + (static_cast<long long>(s) << 7); // *128\n const float* __restrict__ dp = dst_feat + (static_cast<long long>(d) << 7);\n\n float sum = 0.0f;\n float a0 = sp[lane]; float b0 = dp[lane];\n float a1 = sp[lane + 32]; float b1 = dp[lane + 32];\n float a2 = sp[lane + 64]; float b2 = dp[lane + 64];\n float a3 = sp[lane + 96]; float b3 = dp[lane + 96];\n sum = fmaf(a0, b0, sum);\n sum = fmaf(a1, b1, sum);\n sum = fmaf(a2, b2, sum);\n sum = fmaf(a3, b3, sum);\n\n sum += __shfl_down_sync(0xffffffff, sum, 16);\n sum += __shfl_down_sync(0xffffffff, sum, 8);\n sum += __shfl_down_sync(0xffffffff, sum, 4);\n sum += __shfl_down_sync(0xffffffff, sum, 2);\n sum += __shfl_down_sync(0xffffffff, sum, 1);\n if (lane == 0) out[edge] = sum;\n}\n\ntemplate <typename index_t>\n__global__ __launch_bounds__(256, 2)\nvoid edge_dot_generic_kernel(const index_t* __restrict__ src_idx,\n const index_t* __restrict__ dst_idx,\n const float* __restrict__ src_feat,\n const float* __restrict__ dst_feat,\n float* __restrict__ out,\n int E,\n int D) {\n constexpr int WARPS_PER_BLOCK = 8;\n int tid = threadIdx.x;\n int lane = tid & 31;\n int warp = tid >> 5;\n int edge = blockIdx.x * WARPS_PER_BLOCK + warp;\n if (edge >= E) return;\n\n int s = static_cast<int>(src_idx[edge]);\n int d = static_cast<int>(dst_idx[edge]);\n const float* __restrict__ sp = src_feat + static_cast<long long>(s) * D;\n const float* __restrict__ dp = dst_feat + static_cast<long long>(d) * D;\n\n float sum = 0.0f;\n for (int k = lane; k < D; k += 32) {\n sum = fmaf(sp[k], dp[k], sum);\n }\n\n sum += __shfl_down_sync(0xffffffff, sum, 16);\n sum += __shfl_down_sync(0xffffffff, sum, 8);\n sum += __shfl_down_sync(0xffffffff, sum, 4);\n sum += __shfl_down_sync(0xffffffff, sum, 2);\n sum += __shfl_down_sync(0xffffffff, sum, 1);\n if (lane == 0) out[edge] = sum;\n}\n\ntorch::Tensor edge_dot_forward(torch::Tensor src_idx,\n torch::Tensor dst_idx,\n torch::Tensor src_feat,\n torch::Tensor dst_feat) {\n const int E = static_cast<int>(src_idx.numel());\n const int D = static_cast<int>(src_feat.size(1));\n auto out = torch::empty({E}, src_feat.options());\n if (E == 0) return out;\n\n constexpr int THREADS = 256;\n constexpr int WARPS_PER_BLOCK = THREADS / 32;\n dim3 block(THREADS);\n dim3 grid((E + WARPS_PER_BLOCK - 1) / WARPS_PER_BLOCK);\n cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n\n if (src_idx.scalar_type() == at::kInt) {\n const int* sidx = src_idx.data_ptr<int>();\n const int* didx = dst_idx.data_ptr<int>();\n if (D == 128) {\n edge_dot128_kernel<int><<<grid, block, 0, stream>>>(sidx, didx,\n src_feat.data_ptr<float>(), dst_feat.data_ptr<float>(), out.data_ptr<float>(), E);\n } else {\n edge_dot_generic_kernel<int><<<grid, block, 0, stream>>>(sidx, didx,\n src_feat.data_ptr<float>(), dst_feat.data_ptr<float>(), out.data_ptr<float>(), E, D);\n }\n } else { // int64 indices\n const int64_t* sidx = src_idx.data_ptr<int64_t>();\n const int64_t* didx = dst_idx.data_ptr<int64_t>();\n if (D == 128) {\n edge_dot128_kernel<int64_t><<<grid, block, 0, stream>>>(sidx, didx,\n src_feat.data_ptr<float>(), dst_feat.data_ptr<float>(), out.data_ptr<float>(), E);\n } else {\n edge_dot_generic_kernel<int64_t><<<grid, block, 0, stream>>>(sidx, didx,\n src_feat.data_ptr<float>(), dst_feat.data_ptr<float>(), out.data_ptr<float>(), E, D);\n }\n }\n C10_CUDA_KERNEL_LAUNCH_CHECK();\n return out;\n}\n'''\n\n_ext = load_inline(\n name=\"edge_sampled_dot_ext_v1\",\n cpp_sources=_cpp_src,\n cuda_sources=_cuda_src,\n functions=[\"edge_dot_forward\"],\n extra_cflags=[\"-O3\"],\n extra_cuda_cflags=[\"-O3\"],\n with_cuda=True,\n verbose=False,\n)\n\n\nclass ModelNew(nn.Module):\n def __init__(self):\n super().__init__()\n\n def forward(self, src_idx: torch.Tensor, dst_idx: torch.Tensor,\n src_feat: torch.Tensor, dst_feat: torch.Tensor) -> torch.Tensor:\n return _ext.edge_dot_forward(src_idx, dst_idx, src_feat, dst_feat)\n" }
result ← submit_kernel (fail)
failsubmit_kernel submit_kernel FAILED: correctness check did not pass (? trials). runtime_error: expected scalar type Long but found Float
final submitted kernel (152 lines, 5,649 chars) — full source, untruncated
import torch
import torch.nn as nn
from torch.utils.cpp_extension import load_inline

_cpp_src = r'''
#include <torch/extension.h>

torch::Tensor edge_dot_forward(torch::Tensor src_idx,
                               torch::Tensor dst_idx,
                               torch::Tensor src_feat,
                               torch::Tensor dst_feat);
'''

_cuda_src = r'''
#include <torch/extension.h>
#include <ATen/cuda/CUDAContext.h>
#include <c10/cuda/CUDAException.h>
#include <cstdint>

template <typename index_t>
__global__ __launch_bounds__(256, 2)
void edge_dot128_kernel(const index_t* __restrict__ src_idx,
                        const index_t* __restrict__ dst_idx,
                        const float* __restrict__ src_feat,
                        const float* __restrict__ dst_feat,
                        float* __restrict__ out,
                        int E) {
    constexpr int WARPS_PER_BLOCK = 8;
    int tid = threadIdx.x;
    int lane = tid & 31;
    int warp = tid >> 5;
    int edge = blockIdx.x * WARPS_PER_BLOCK + warp;
    if (edge >= E) return;

    int s = static_cast<int>(src_idx[edge]);
    int d = static_cast<int>(dst_idx[edge]);
    const float* __restrict__ sp = src_feat + (static_cast<long long>(s) << 7);  // *128
    const float* __restrict__ dp = dst_feat + (static_cast<long long>(d) << 7);

    float sum = 0.0f;
    float a0 = sp[lane];       float b0 = dp[lane];
    float a1 = sp[lane + 32];  float b1 = dp[lane + 32];
    float a2 = sp[lane + 64];  float b2 = dp[lane + 64];
    float a3 = sp[lane + 96];  float b3 = dp[lane + 96];
    sum = fmaf(a0, b0, sum);
    sum = fmaf(a1, b1, sum);
    sum = fmaf(a2, b2, sum);
    sum = fmaf(a3, b3, sum);

    sum += __shfl_down_sync(0xffffffff, sum, 16);
    sum += __shfl_down_sync(0xffffffff, sum, 8);
    sum += __shfl_down_sync(0xffffffff, sum, 4);
    sum += __shfl_down_sync(0xffffffff, sum, 2);
    sum += __shfl_down_sync(0xffffffff, sum, 1);
    if (lane == 0) out[edge] = sum;
}

template <typename index_t>
__global__ __launch_bounds__(256, 2)
void edge_dot_generic_kernel(const index_t* __restrict__ src_idx,
                             const index_t* __restrict__ dst_idx,
                             const float* __restrict__ src_feat,
                             const float* __restrict__ dst_feat,
                             float* __restrict__ out,
                             int E,
                             int D) {
    constexpr int WARPS_PER_BLOCK = 8;
    int tid = threadIdx.x;
    int lane = tid & 31;
    int warp = tid >> 5;
    int edge = blockIdx.x * WARPS_PER_BLOCK + warp;
    if (edge >= E) return;

    int s = static_cast<int>(src_idx[edge]);
    int d = static_cast<int>(dst_idx[edge]);
    const float* __restrict__ sp = src_feat + static_cast<long long>(s) * D;
    const float* __restrict__ dp = dst_feat + static_cast<long long>(d) * D;

    float sum = 0.0f;
    for (int k = lane; k < D; k += 32) {
        sum = fmaf(sp[k], dp[k], sum);
    }

    sum += __shfl_down_sync(0xffffffff, sum, 16);
    sum += __shfl_down_sync(0xffffffff, sum, 8);
    sum += __shfl_down_sync(0xffffffff, sum, 4);
    sum += __shfl_down_sync(0xffffffff, sum, 2);
    sum += __shfl_down_sync(0xffffffff, sum, 1);
    if (lane == 0) out[edge] = sum;
}

torch::Tensor edge_dot_forward(torch::Tensor src_idx,
                               torch::Tensor dst_idx,
                               torch::Tensor src_feat,
                               torch::Tensor dst_feat) {
    const int E = static_cast<int>(src_idx.numel());
    const int D = static_cast<int>(src_feat.size(1));
    auto out = torch::empty({E}, src_feat.options());
    if (E == 0) return out;

    constexpr int THREADS = 256;
    constexpr int WARPS_PER_BLOCK = THREADS / 32;
    dim3 block(THREADS);
    dim3 grid((E + WARPS_PER_BLOCK - 1) / WARPS_PER_BLOCK);
    cudaStream_t stream = at::cuda::getCurrentCUDAStream();

    if (src_idx.scalar_type() == at::kInt) {
        const int* sidx = src_idx.data_ptr<int>();
        const int* didx = dst_idx.data_ptr<int>();
        if (D == 128) {
            edge_dot128_kernel<int><<<grid, block, 0, stream>>>(sidx, didx,
                src_feat.data_ptr<float>(), dst_feat.data_ptr<float>(), out.data_ptr<float>(), E);
        } else {
            edge_dot_generic_kernel<int><<<grid, block, 0, stream>>>(sidx, didx,
                src_feat.data_ptr<float>(), dst_feat.data_ptr<float>(), out.data_ptr<float>(), E, D);
        }
    } else { // int64 indices
        const int64_t* sidx = src_idx.data_ptr<int64_t>();
        const int64_t* didx = dst_idx.data_ptr<int64_t>();
        if (D == 128) {
            edge_dot128_kernel<int64_t><<<grid, block, 0, stream>>>(sidx, didx,
                src_feat.data_ptr<float>(), dst_feat.data_ptr<float>(), out.data_ptr<float>(), E);
        } else {
            edge_dot_generic_kernel<int64_t><<<grid, block, 0, stream>>>(sidx, didx,
                src_feat.data_ptr<float>(), dst_feat.data_ptr<float>(), out.data_ptr<float>(), E, D);
        }
    }
    C10_CUDA_KERNEL_LAUNCH_CHECK();
    return out;
}
'''

_ext = load_inline(
    name="edge_sampled_dot_ext_v1",
    cpp_sources=_cpp_src,
    cuda_sources=_cuda_src,
    functions=["edge_dot_forward"],
    extra_cflags=["-O3"],
    extra_cuda_cflags=["-O3"],
    with_cuda=True,
    verbose=False,
)


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

    def forward(self, src_idx: torch.Tensor, dst_idx: torch.Tensor,
                src_feat: torch.Tensor, dst_feat: torch.Tensor) -> torch.Tensor:
        return _ext.edge_dot_forward(src_idx, dst_idx, src_feat, dst_feat)