correct
22_FusedBallQuery3D.py
started
2026-05-05T00:22:11Z
timingkernelbench.timing
kernel mean / median / std
0.01 / — / 0.01 μs
ref mean / median / std
0.23 / — / 0.04 μs
numerical precisioneval_kernel_against_ref
gpu memoryextended_metrics.measure_memory
kernel launches / fusiontorch.profiler
fusion ratio (ref / kernel)
89.00x
SOL (speed-of-light)nsight_hardware_counters
roofline / occupancynsight_hardware_counters
warp stalls (top 5)nsight
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)