gpt-5.5-priority · popcorn · l123_single_gpt
correct 22_FusedBallQuery3D.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
0.01 μs
ref runtime
0.23 μs
speedup
19.48x
started
2026-05-05T00:22:11Z
timingkernelbench.timing
kernel runtime
0.01 μs
ref runtime
0.23 μs
speedup
19.48x
kernel mean / median / std
0.01 / — / 0.01 μs
ref mean / median / std
0.23 / — / 0.04 μs
numerical precisioneval_kernel_against_ref
max abs error
0.00
mean abs error
0.00
max rel error
0.00
mean rel error
0.00
gpu memoryextended_metrics.measure_memory
kernel peak
33.31 MB
ref peak
51.00 MB
kernel / ref
0.65x
kernel launches / fusiontorch.profiler
kernel launches
1
ref launches
89
fusion ratio (ref / kernel)
89.00x
total cuda time
23 μs
SOL (speed-of-light)nsight_hardware_counters
SOL score (0–1)
0.001
dram util %
0.1%
compute util %
0.0%
bottleneck
unknown
dominant pipe
None
arithmetic intensity
—
ridge point
20.00
roofline / occupancynsight_hardware_counters
dram bandwidth
4.15 GB/s
dram util %
0.1%
fp32 tflops
—
fp32 util %
—
fp16 tflops
—
occupancy %
20.6%
bottleneck
unknown
dominant pipe %
—
arithmetic intensity
—
ridge point
20.00
L1 hit %
—
L2 hit %
96.4%
ld sectors / req
8.12
st sectors / req
—
regs / thread
20
smem / block
1280 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
1316.9%
short_scoreboard
128.1%
mio_throttle
66.7%
flagssubmit_kernel guard
excessive speedup
flagged
reference PyTorch source (the prompt the agent sees)
import torch
import torch.nn as nn

# Default ModelNew is PyTorch-only. run_and_check: use check_kernel=False with backend=cuda
# until the candidate file contains a real CUDA kernel (see kernel_static_checker).


class Model(nn.Module):
    """
    Fused radius (ball) neighborhood query over a 3D point map with per-query feature
    aggregation—common in navigation / manipulation perception (PointNet++-style set
    abstraction, local maps around end-effector or goal queries on LiDAR / depth).

    Single forward fusion target:
      1) pairwise Euclidean distances between each query center and every point,
      2) membership in the closed ball of radius ``r``,
      3) channel-wise **max** pooling of point features over in-ball neighbors.

    Points with no neighbors inside the radius contribute a **zero** feature vector
    for that query (instead of ``-inf`` max).
    """

    def __init__(self, in_channels: int, radius: float):
        super().__init__()
        self.in_channels = in_channels
        self.register_buffer("radius_sq", torch.tensor(float(radius) ** 2))

    def forward(
        self,
        points: torch.Tensor,
        queries: torch.Tensor,
        features: torch.Tensor,
    ) -> torch.Tensor:
        """
        Args:
            points: (B, P, 3) point coordinates (e.g. world or sensor frame).
            queries: (B, Q, 3) ball centers (e.g. navigation waypoints, grasp samples).
            features: (B, P, C) per-point descriptors (geometry or lifted RGB-D).
        Returns:
            (B, Q, C) max-pooled features over neighbors with ||p - q||_2 <= r.
        """
        B, P, _ = points.shape
        _, Q, _ = queries.shape
        _, P2, C = features.shape
        assert P == P2 and C == self.in_channels

        # Squared distances (B, Q, P)
        dist_sq = torch.cdist(queries, points, p=2.0) ** 2
        in_ball = dist_sq <= self.radius_sq

        # (B, 1, P, C) broadcasts with (B, Q, P) for masked max over P
        feats = features.unsqueeze(1)
        neg_large = torch.finfo(features.dtype).min
        masked = torch.where(
            in_ball.unsqueeze(-1),
            feats,
            torch.full_like(feats, neg_large),
        )
        pooled, _ = masked.max(dim=2)

        empty = in_ball.sum(dim=2) == 0
        if empty.any():
            pooled = pooled.masked_fill(empty.unsqueeze(-1), 0.0)

        return pooled


class ModelNew(nn.Module):
    """KernelBench candidate entry point; default delegates to ``Model``."""

    def __init__(self, in_channels: int, radius: float):
        super().__init__()
        self._impl = Model(in_channels, radius)

    def forward(
        self,
        points: torch.Tensor,
        queries: torch.Tensor,
        features: torch.Tensor,
    ) -> torch.Tensor:
        return self._impl(points, queries, features)


batch_size = 4
num_points = 512
num_queries = 64
in_channels = 32
radius = 0.12


def get_inputs():
    p = popcorn_pri
    points = torch.randn(p.jitter_int(batch_size), p.jitter_int(num_points), 3)
    queries = torch.randn(p.jitter_int(batch_size), p.jitter_int(num_queries), 3)
    features = torch.randn(p.jitter_int(batch_size), p.jitter_int(num_points), in_channels)
    return [points, queries, features]


def get_init_inputs():
    return [in_channels, radius]
turn 0 · FINAL · 1 tool call(s) · 43.0s turn · 43.0s LLM · 7,909 chars (click to expand)
turn 043.0s turn · 43.0s LLM · 0.0s tools · FINAL
reasoning
**Evaluating code implementation** I need to produce code that compiles inline and perhaps requires fixed sizes. There's a kernel benchmark that often uses fixed sizes, so I might need to handle arbitrary contiguous CUDA fp32 data. I plan to implement a max over P for each B, Q, C with specific values: B=4, Q=64, C=32, and P=512. The overall goal is to compute distances efficiently, optimizing distance calculations, and managing threading effectively for performance. Each step seems crucial for achieving my performance goals. **Handling CUDA implementation** I need to manage empty zero features in fp32 and ensure points for queries are contiguous. I can use a CUDA extension with a host function to allocate output and launch it. Using Torch in Python without pybind makes sense. For C++ sources, I’ll include a wrapper declaration to load inline with the necessary sources. I'll optimize the kernel dimensions based on the number of threads while supporting a range of channel values. If C is 32, that's straightforward with one warp, and I can set up threads accordingly! **Initializing channel variables** I need to handle an empty sentinel boolean found per channel, which will be consistent across channels. So, I’ll initialize the maximum value to -FLT_MAX and set found to false. For each point, if it’s within the ball, I should update found to true and check if I need to update max to the maximum between the current max and the feature. Also, I need to be careful if any feature has a value of -inf. That might affect my calculations! **Evaluating computation strategy** I’m analyzing the distance computations for each channel lane. It seems like duplicating the process for 32 times creates unnecessary waste. A better approach might be to assign a block for each batch query with threads that calculate distances once and aggregate results. Since there are 32 channels, I could utilize a shared array for local maxima, but it might get complicated with atomic functions. I need to weigh the pros and cons of these strategies carefully! **Refining distance computation** I’m initializing a shared maximum for 32 channels to -FLT_MAX and assigning a count. Each thread will check if it's within the ball and use atomicMaxFloat for the shared maximum. This minimizes the need for frequent atomic operations, which should only occur rarely. By computing distances once per batch query, it speeds up the process significantly—essentially 131k distance calculations in a single go. It feels efficient and clear!
call → submit_kernel
callsubmit_kernel { "kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\n\n_cpp_src = r\"\"\"\n#include <torch/extension.h>\n\ntorch::Tensor ball_max_forward(torch::Tensor points, torch::Tensor queries, torch::Tensor features, double radius_sq);\n\"\"\"\n\n_cuda_src = r\"\"\"\n#include <torch/extension.h>\n#include <ATen/cuda/CUDAContext.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <cfloat>\n\n__device__ __forceinline__ void atomicMaxFloatShared(float* addr, float value) {\n int* addr_i = reinterpret_cast<int*>(addr);\n int old = *addr_i;\n int assumed;\n // CAS loop using floating point comparison, works for negative values too.\n while (value > __int_as_float(old)) {\n assumed = old;\n old = atomicCAS(addr_i, assumed, __float_as_int(value));\n if (old == assumed) break;\n }\n}\n\n__global__ void ball_max_sparse_kernel(\n const float* __restrict__ points,\n const float* __restrict__ queries,\n const float* __restrict__ features,\n float* __restrict__ out,\n int B, int P, int Q, int C,\n float radius_sq)\n{\n int b = blockIdx.x;\n int q = blockIdx.y;\n int tid = threadIdx.x;\n\n extern __shared__ unsigned char smem[];\n float* smax = reinterpret_cast<float*>(smem);\n int* sfound = reinterpret_cast<int*>(smax + C);\n\n for (int c = tid; c < C; c += blockDim.x) {\n smax[c] = -FLT_MAX;\n }\n if (tid == 0) *sfound = 0;\n __syncthreads();\n\n const float qx = queries[(b * Q + q) * 3 + 0];\n const float qy = queries[(b * Q + q) * 3 + 1];\n const float qz = queries[(b * Q + q) * 3 + 2];\n\n const int point_base = b * P * 3;\n const int feat_base = b * P * C;\n\n for (int p = tid; p < P; p += blockDim.x) {\n float dx = points[point_base + p * 3 + 0] - qx;\n float dy = points[point_base + p * 3 + 1] - qy;\n float dz = points[point_base + p * 3 + 2] - qz;\n float d2 = dx * dx + dy * dy + dz * dz;\n if (d2 <= radius_sq) {\n atomicExch(sfound, 1);\n const float* fptr = features + feat_base + p * C;\n // In the benchmark C=32, so this is a short, rare loop because the radius is small.\n for (int c = 0; c < C; ++c) {\n atomicMaxFloatShared(smax + c, fptr[c]);\n }\n }\n }\n __syncthreads();\n\n const int out_base = (b * Q + q) * C;\n if (*sfound) {\n for (int c = tid; c < C; c += blockDim.x) {\n out[out_base + c] = smax[c];\n }\n } else {\n for (int c = tid; c < C; c += blockDim.x) {\n out[out_base + c] = 0.0f;\n }\n }\n}\n\ntorch::Tensor ball_max_forward(torch::Tensor points, torch::Tensor queries, torch::Tensor features, double radius_sq) {\n const int B = (int)points.size(0);\n const int P = (int)points.size(1);\n const int Q = (int)queries.size(1);\n const int C = (int)features.size(2);\n\n auto out = torch::empty({B, Q, C}, features.options());\n\n dim3 grid(B, Q);\n const int threads = 256;\n size_t shmem = (size_t)C * sizeof(float) + sizeof(int);\n cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n ball_max_sparse_kernel<<<grid, threads, shmem, stream>>>(\n points.data_ptr<float>(), queries.data_ptr<float>(), features.data_ptr<float>(), out.data_ptr<float>(),\n B, P, Q, C, (float)radius_sq);\n return out;\n}\n\"\"\"\n\n_ext = load_inline(\n name=\"ball_max_pool_ext_v1\",\n cpp_sources=_cpp_src,\n cuda_sources=_cuda_src,\n functions=[\"ball_max_forward\"],\n extra_cuda_cflags=[\"-O3\", \"--use_fast_math\"],\n extra_cflags=[\"-O3\"],\n verbose=False,\n)\n\n\nclass ModelNew(nn.Module):\n def __init__(self, in_channels: int, radius: float):\n super().__init__()\n self.in_channels = in_channels\n self.radius_sq = float(radius) * float(radius)\n\n def forward(self, points: torch.Tensor, queries: torch.Tensor, features: torch.Tensor) -> torch.Tensor:\n return _ext.ball_max_forward(points, queries, features, self.radius_sq)\n" }
result ← submit_kernel (ok)
oksubmit_kernel submit_kernel PASSED: (5 / 5) correctness trials all passed. Kernel runtime: 0.01 μs Runtime stats: mean=0.01μs median=?μs std=0.01μs Numerical precision: max_abs_err=0.00e+00 mean_abs_err=0.00e+00 max_rel_err=0.00e+00 Memory: 33.31 MB peak (ref: 51.0 MB, ratio: 0.6531x) Kernel launches: 1 (ref: 89, fusion ratio: 89.0) SOL score: 0.0012 Flagged for excessive speedup — this submission may have been rejected by automated review.
final submitted kernel (126 lines, 3,962 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 ball_max_forward(torch::Tensor points, torch::Tensor queries, torch::Tensor features, double radius_sq);
"""

_cuda_src = r"""
#include <torch/extension.h>
#include <ATen/cuda/CUDAContext.h>
#include <cuda.h>
#include <cuda_runtime.h>
#include <cfloat>

__device__ __forceinline__ void atomicMaxFloatShared(float* addr, float value) {
    int* addr_i = reinterpret_cast<int*>(addr);
    int old = *addr_i;
    int assumed;
    // CAS loop using floating point comparison, works for negative values too.
    while (value > __int_as_float(old)) {
        assumed = old;
        old = atomicCAS(addr_i, assumed, __float_as_int(value));
        if (old == assumed) break;
    }
}

__global__ void ball_max_sparse_kernel(
    const float* __restrict__ points,
    const float* __restrict__ queries,
    const float* __restrict__ features,
    float* __restrict__ out,
    int B, int P, int Q, int C,
    float radius_sq)
{
    int b = blockIdx.x;
    int q = blockIdx.y;
    int tid = threadIdx.x;

    extern __shared__ unsigned char smem[];
    float* smax = reinterpret_cast<float*>(smem);
    int* sfound = reinterpret_cast<int*>(smax + C);

    for (int c = tid; c < C; c += blockDim.x) {
        smax[c] = -FLT_MAX;
    }
    if (tid == 0) *sfound = 0;
    __syncthreads();

    const float qx = queries[(b * Q + q) * 3 + 0];
    const float qy = queries[(b * Q + q) * 3 + 1];
    const float qz = queries[(b * Q + q) * 3 + 2];

    const int point_base = b * P * 3;
    const int feat_base = b * P * C;

    for (int p = tid; p < P; p += blockDim.x) {
        float dx = points[point_base + p * 3 + 0] - qx;
        float dy = points[point_base + p * 3 + 1] - qy;
        float dz = points[point_base + p * 3 + 2] - qz;
        float d2 = dx * dx + dy * dy + dz * dz;
        if (d2 <= radius_sq) {
            atomicExch(sfound, 1);
            const float* fptr = features + feat_base + p * C;
            // In the benchmark C=32, so this is a short, rare loop because the radius is small.
            for (int c = 0; c < C; ++c) {
                atomicMaxFloatShared(smax + c, fptr[c]);
            }
        }
    }
    __syncthreads();

    const int out_base = (b * Q + q) * C;
    if (*sfound) {
        for (int c = tid; c < C; c += blockDim.x) {
            out[out_base + c] = smax[c];
        }
    } else {
        for (int c = tid; c < C; c += blockDim.x) {
            out[out_base + c] = 0.0f;
        }
    }
}

torch::Tensor ball_max_forward(torch::Tensor points, torch::Tensor queries, torch::Tensor features, double radius_sq) {
    const int B = (int)points.size(0);
    const int P = (int)points.size(1);
    const int Q = (int)queries.size(1);
    const int C = (int)features.size(2);

    auto out = torch::empty({B, Q, C}, features.options());

    dim3 grid(B, Q);
    const int threads = 256;
    size_t shmem = (size_t)C * sizeof(float) + sizeof(int);
    cudaStream_t stream = at::cuda::getCurrentCUDAStream();
    ball_max_sparse_kernel<<<grid, threads, shmem, stream>>>(
        points.data_ptr<float>(), queries.data_ptr<float>(), features.data_ptr<float>(), out.data_ptr<float>(),
        B, P, Q, C, (float)radius_sq);
    return out;
}
"""

_ext = load_inline(
    name="ball_max_pool_ext_v1",
    cpp_sources=_cpp_src,
    cuda_sources=_cuda_src,
    functions=["ball_max_forward"],
    extra_cuda_cflags=["-O3", "--use_fast_math"],
    extra_cflags=["-O3"],
    verbose=False,
)


class ModelNew(nn.Module):
    def __init__(self, in_channels: int, radius: float):
        super().__init__()
        self.in_channels = in_channels
        self.radius_sq = float(radius) * float(radius)

    def forward(self, points: torch.Tensor, queries: torch.Tensor, features: torch.Tensor) -> torch.Tensor:
        return _ext.ball_max_forward(points, queries, features, self.radius_sq)