gpt-5.5-priority · original · l3_default_gpt
correct 46_NetVladWithGhostClusters.py
model
gpt-5.5-priority
turns / max
9 / 15
tool calls
9
tokens (total)
0
wall clock
—
truncated
no
backend
cuda fp32
variant
original
runtime
2.52 μs
ref runtime
1.60 μs
speedup
0.63x
started
2026-05-04T21:12:07Z
timingkernelbench.timing
kernel runtime
2.52 μs
ref runtime
1.60 μs
speedup
0.63x
kernel mean / median / std
2.52 / — / 0.60 μs
ref mean / median / std
1.60 / — / 0.17 μs
numerical precisioneval_kernel_against_ref
max abs error
5.59e-09
mean abs error
9.97e-10
max rel error
7.57e-07
mean rel error
1.29e-07
gpu memoryextended_metrics.measure_memory
kernel peak
756.57 MB
ref peak
855.82 MB
kernel / ref
0.88x
kernel launches / fusiontorch.profiler
kernel launches
22
ref launches
59
fusion ratio (ref / kernel)
2.68x
total cuda time
1325 μs
energyNVML (pynvml)
kernel mJ / run
254.34
ref mJ / run
1016.80
ratio (ref / kernel)
4.00x
avg power
103.71 W
ref avg power
641.43 W
SOL (speed-of-light)nsight_hardware_counters
SOL score (0–1)
1.000
dram util %
100.0%
compute util %
100.0%
bottleneck
compute-bound
dominant pipe
FP32
arithmetic intensity
—
ridge point
20.00
roofline / occupancynsight_hardware_counters
dram bandwidth
7525.45 GB/s
dram util %
100.0%
fp32 tflops
—
fp32 util %
—
fp16 tflops
—
occupancy %
409.1%
bottleneck
compute-bound
dominant pipe %
169.4%
arithmetic intensity
—
ridge point
20.00
L1 hit %
—
L2 hit %
632.8%
ld sectors / req
25.35
st sectors / req
—
regs / thread
406
smem / block
29184 B
block size
2208
peak bw
3350.00 GB/s
peak fp32 tflops
67.00
peak fp16 tflops
1979.00
warp stalls (top 5)nsight
long_scoreboard
9451.7%
short_scoreboard
1232.3%
mio_throttle
89.5%
reference PyTorch source (the prompt the agent sees)
# Copyright 2018 Antoine Miech All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS-IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
Code modified from here
https://github.com/albanie/collaborative-experts/blob/master/model/net_vlad.py
"""


import math
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch as th


class Model(nn.Module):
    def __init__(self, cluster_size, feature_size, ghost_clusters):
        super(Model, self).__init__()

        self.feature_size = feature_size
        self.cluster_size = cluster_size
        self.ghost_clusters = ghost_clusters

        init_sc = (1 / math.sqrt(feature_size))
        clusters = cluster_size + ghost_clusters

        # The `clusters` weights are the `(w,b)` in the paper
        self.clusters = nn.Parameter(init_sc * th.randn(feature_size, clusters))
        self.batch_norm = nn.BatchNorm1d(clusters)
        # The `clusters2` weights are the visual words `c_k` in the paper
        self.clusters2 = nn.Parameter(init_sc * th.randn(1, feature_size, cluster_size))
        self.out_dim = self.cluster_size * feature_size

    def forward(self, x, mask=None):
        """Aggregates feature maps into a fixed size representation.  In the following
        notation, B = batch_size, N = num_features, K = num_clusters, D = feature_size.

        Args:
            x (th.Tensor): B x N x D

        Returns:
            (th.Tensor): B x DK
        """
        max_sample = x.size()[1]
        x = x.view(-1, self.feature_size)  # B x N x D -> BN x D

        if x.device != self.clusters.device:
            msg = f"x.device {x.device} != cluster.device {self.clusters.device}"
            raise ValueError(msg)

        assignment = th.matmul(x, self.clusters)  # (BN x D) x (D x (K+G)) -> BN x (K+G)
        assignment = self.batch_norm(assignment)

        assignment = F.softmax(assignment, dim=1)  # BN x (K+G) -> BN x (K+G)
        # remove ghost assigments
        assignment = assignment[:, :self.cluster_size]
        assignment = assignment.view(-1, max_sample, self.cluster_size)  # -> B x N x K
        a_sum = th.sum(assignment, dim=1, keepdim=True)  # B x N x K -> B x 1 x K
        a = a_sum * self.clusters2

        assignment = assignment.transpose(1, 2)  # B x N x K -> B x K x N

        x = x.view(-1, max_sample, self.feature_size)  # BN x D -> B x N x D
        vlad = th.matmul(assignment, x)  # (B x K x N) x (B x N x D) -> B x K x D
        vlad = vlad.transpose(1, 2)  # -> B x D x K
        vlad = vlad - a

        # L2 intra norm
        vlad = F.normalize(vlad)

        # flattening + L2 norm
        vlad = vlad.reshape(-1, self.cluster_size * self.feature_size)  # -> B x DK
        vlad = F.normalize(vlad)
        return vlad  # B x DK

batch_size = 2048
num_features = 100
num_clusters = 32
feature_size = 512
ghost_clusters = 16

def get_inputs():
  return [torch.rand(batch_size, num_features, feature_size)]

def get_init_inputs():
  return [num_clusters, feature_size, ghost_clusters]
turn 011.9s turn · 11.9s LLM · 0.0s tools
call → get_gpu_specs
callget_gpu_specs {}
result ← get_gpu_specs (ok)
okget_gpu_specs GPU specs for NVIDIA H100 80GB HBM3: total memory (runtime): 79.1 GB spec entry: H100 GPU Architecture: Hopper GPU Memory: 80GB Memory Bandwidth: 3.35 TB/s FP64 TFLOPS: 34 FP64 Tensor Core TFLOPS: 67 FP32 TFLOPS: 67 TF32 Tensor Core TFLOPS: 989 with sparsity BFLOAT16 Tensore Core TFLOPS: 1979 with sparsity FP16 Tensor Core TFLOPS: 1979 with sparsity FP8 Tensor Core TFLOPS: 3958 with sparsity INT8 Tensor Core TOPS: 3958 with sparsity Register File Size: 64K 32-bit registers per SM Maximum number of registers per thread: 255 Maximum number of thread blocks per SM: 32 Shared memory capacity per SM: 228 KB Maximum shared memory per thread block: 227 KB
turn 1 · 1 tool call(s) · 49.5s turn · 49.5s LLM · 11,124 chars (click to expand)
turn 149.5s turn · 49.5s LLM · 0.0s tools
reasoning
**Deciding on calculations for softmax** I'm focusing on how to store \( p \) for K, using the numerator with the softmax formula. It feels good to consider using `__expf` for approximation, thinking about a tolerance of \( 1e-4 \). But, is the error acceptable? For accuracy, perhaps using `expf` makes more sense. I noticed performance isn't a huge concern at \( 204k \times 48 \). Also, I'll need to ensure that `ModelNew` defines `batch_norm` and check if `.to(device)` is working as expected. Let's run some tests!
call → compile_kernel
callcompile_kernel { "kernel_code": "import math\nimport torch\nimport torch.nn as nn\nimport torch as th\nfrom torch.utils.cpp_extension import load_inline\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 <vector>\n#include <math.h>\n\n#define D_TILE 16\n#define K_TILE 16\n\n__inline__ __device__ float warp_reduce_sum(float v) {\n for (int offset = 16; offset > 0; offset >>= 1) v += __shfl_down_sync(0xffffffff, v, offset);\n return v;\n}\n\n__global__ void softmax_keep_kernel(const float* __restrict__ logits,\n float* __restrict__ probs,\n int rows, int C, int K) {\n int row = blockIdx.x;\n int tid = threadIdx.x;\n if (row >= rows) return;\n const float* in = logits + row * C;\n float m = -INFINITY;\n for (int c = tid; c < C; c += blockDim.x) {\n float v = in[c];\n m = fmaxf(m, v);\n }\n // reduce max in block (64 threads)\n __shared__ float smem[64];\n smem[tid] = m;\n __syncthreads();\n for (int s = blockDim.x >> 1; s > 0; s >>= 1) {\n if (tid < s) smem[tid] = fmaxf(smem[tid], smem[tid+s]);\n __syncthreads();\n }\n m = smem[0];\n float sum = 0.0f;\n for (int c = tid; c < C; c += blockDim.x) sum += expf(in[c] - m);\n smem[tid] = sum;\n __syncthreads();\n for (int s = blockDim.x >> 1; s > 0; s >>= 1) {\n if (tid < s) smem[tid] += smem[tid+s];\n __syncthreads();\n }\n float inv = 1.0f / smem[0];\n for (int c = tid; c < K; c += blockDim.x) probs[row * K + c] = expf(in[c] - m) * inv;\n}\n\n__global__ void raw_vlad_kernel(const float* __restrict__ x,\n const float* __restrict__ probs,\n const float* __restrict__ c2,\n float* __restrict__ raw,\n float* __restrict__ partial,\n int B, int N, int D, int K, int num_dtiles) {\n int tx = threadIdx.x; // d within tile\n int ty = threadIdx.y; // k within tile\n int d = blockIdx.x * D_TILE + tx;\n int k = blockIdx.y * K_TILE + ty;\n int b = blockIdx.z;\n float val = 0.0f;\n if (d < D && k < K) {\n float acc = 0.0f;\n float ps = 0.0f;\n const float* xb = x + (long long)b * N * D + d;\n const float* pb = probs + (long long)b * N * K + k;\n #pragma unroll 4\n for (int n = 0; n < N; ++n) {\n float p = pb[(long long)n * K];\n ps += p;\n acc += p * xb[(long long)n * D];\n }\n val = acc - ps * c2[d * K + k];\n raw[((long long)b * D + d) * K + k] = val;\n }\n __shared__ float ss[K_TILE][D_TILE];\n ss[ty][tx] = val * val;\n __syncthreads();\n if (tx < 8) ss[ty][tx] += ss[ty][tx+8];\n __syncthreads();\n if (tx < 4) ss[ty][tx] += ss[ty][tx+4];\n __syncthreads();\n if (tx < 2) ss[ty][tx] += ss[ty][tx+2];\n __syncthreads();\n if (tx == 0) {\n float s = ss[ty][0] + ss[ty][1];\n if (k < K) partial[((long long)b * K + k) * num_dtiles + blockIdx.x] = s;\n }\n}\n\n__global__ void norm_kernel(const float* __restrict__ raw,\n const float* __restrict__ partial,\n float* __restrict__ out,\n int B, int D, int K, int num_dtiles) {\n int b = blockIdx.x;\n int k = blockIdx.y;\n int tid = threadIdx.x;\n float norm2 = 0.0f;\n for (int t = tid; t < num_dtiles; t += blockDim.x) {\n norm2 += partial[((long long)b * K + k) * num_dtiles + t];\n }\n __shared__ float sm[256];\n sm[tid] = norm2;\n __syncthreads();\n for (int s = blockDim.x >> 1; s > 0; s >>= 1) {\n if (tid < s) sm[tid] += sm[tid+s];\n __syncthreads();\n }\n float n2 = sm[0];\n float inv = 0.0f;\n if (n2 > 1.0e-24f) inv = rsqrtf(n2) * rsqrtf((float)K);\n for (int d = tid; d < D; d += blockDim.x) {\n long long idx = ((long long)b * D + d) * K + k;\n out[idx] = raw[idx] * inv;\n }\n}\n\ntorch::Tensor vlad_forward_cuda(torch::Tensor x, torch::Tensor logits, torch::Tensor clusters2, int64_t B, int64_t N, int64_t D, int64_t K, int64_t C) {\n auto opts = x.options();\n auto probs = torch::empty({B * N, K}, opts);\n auto raw = torch::empty({B, D, K}, opts);\n int64_t num_dtiles = (D + D_TILE - 1) / D_TILE;\n auto partial = torch::empty({B, K, num_dtiles}, opts);\n auto out = torch::empty({B, D * K}, opts);\n cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n softmax_keep_kernel<<<(int)(B*N), 64, 0, stream>>>(logits.data_ptr<float>(), probs.data_ptr<float>(), (int)(B*N), (int)C, (int)K);\n dim3 block(D_TILE, K_TILE);\n dim3 grid((unsigned)num_dtiles, (unsigned)((K + K_TILE - 1)/K_TILE), (unsigned)B);\n raw_vlad_kernel<<<grid, block, 0, stream>>>(x.data_ptr<float>(), probs.data_ptr<float>(), clusters2.data_ptr<float>(), raw.data_ptr<float>(), partial.data_ptr<float>(), (int)B, (int)N, (int)D, (int)K, (int)num_dtiles);\n dim3 grid2((unsigned)B, (unsigned)K);\n norm_kernel<<<grid2, 256, 0, stream>>>(raw.data_ptr<float>(), partial.data_ptr<float>(), out.data_ptr<float>(), (int)B, (int)D, (int)K, (int)num_dtiles);\n return out;\n}\n'''\n\n_ext = load_inline(name='netvlad_ext_v1', cpp_sources='', cuda_sources=_cuda_src,\n functions=['vlad_forward_cuda'],\n extra_cuda_cflags=['-O3', '--use_fast_math'],\n extra_cflags=['-O3'], verbose=False)\n\nclass ModelNew(nn.Module):\n def __init__(self, cluster_size, feature_size, ghost_clusters):\n super(ModelNew, self).__init__()\n self.feature_size = feature_size\n self.cluster_size = cluster_size\n self.ghost_clusters = ghost_clusters\n init_sc = (1 / math.sqrt(feature_size))\n clusters = cluster_size + ghost_clusters\n self.clusters = nn.Parameter(init_sc * th.randn(feature_size, clusters))\n self.batch_norm = nn.BatchNorm1d(clusters)\n self.clusters2 = nn.Parameter(init_sc * th.randn(1, feature_size, cluster_size))\n self.out_dim = self.cluster_size * feature_size\n def forward(self, x, mask=None):\n B = x.size(0); N = x.size(1); D = self.feature_size; K = self.cluster_size; C = K + self.ghost_clusters\n x2 = x.contiguous().view(-1, D)\n assignment = th.matmul(x2, self.clusters)\n assignment = self.batch_norm(assignment)\n return _ext.vlad_forward_cuda(x.contiguous(), assignment.contiguous(), self.clusters2.contiguous(), B, N, D, K, C)\n" }
result ← compile_kernel (fail)
failcompile_kernel compile_kernel FAILED: RuntimeError. RuntimeError: Error building extension 'netvlad_ext_v1': [1/3] /opt/conda/bin/x86_64-conda-linux-gnu-c++ -MMD -MF main.o.d -DTORCH_EXTENSION_NAME=netvlad_ext_v1 -DTORCH_API_INCLUDE_EXTENSION_H -isystem /scratch/abaner/KernelBench/.venv/lib/python3.10/site-packages/torch/include -isystem /scratch/abaner/KernelBench/.venv/lib/python3.10/site-packages/torch/include/torch/csrc/api/include -isystem /usr/local/cuda/include -isystem /opt/conda/include/python3.10 -fPIC -std=c++17 -O3 -c /scratch/abaner/KernelBench/runs/l3_default_gpt/original/gpt-5.5-priority/level_3_problem_46_cache/k_0f1519993d44/netvlad_ext_v1/main.cpp -o main.o FAILED: [code=1] main.o /opt/conda/bin/x86_64-conda-linux-gnu-c++ -MMD -MF main.o.d -DTORCH_EXTENSION_NAME=netvlad_ext_v1 -DTORCH_API_INCLUDE_EXTENSION_H -isystem /scratch/abaner/KernelBench/.venv/lib/python3.10/site-packages/torch/include -isystem /scratch/abaner/KernelBench/.venv/lib/python3.10/site-packages/torch/include/torch/csrc/api/include -isystem /usr/local/cuda/include -isystem /opt/conda/include/python3.10 -fPIC -std=c++17 -O3 -c /scratch/abaner/KernelBench/runs/l3_default_gpt/original/gpt-5.5-priority/level_3_problem_46_cache/k_0f1519993d44/netvlad_ext_v1/main.cpp -o main.o /scratch/abaner/KernelBench/runs/l3_default_gpt/original/gpt-5.5-priority/level_3_problem_46_cache/k_0f1519993d44/netvlad_ext_v1/main.cpp: In function 'void pybind11_init_netvlad_ext_v1(pybind11::module_&)': /scratch/abaner/KernelBench/runs/l3_default_gpt/original/gpt-5.5-priority/level_3_problem_46_cache/k_0f1519993d44/netvlad_ext_v1/main.cpp:4:56: error: 'vlad_forward_cuda' was not declared in this scope 4 | m.def("vlad_forward_cuda", torch::wrap_pybind_function(vlad_forward_cuda), "vlad_forward_cuda"); | ^~~~~~~~~~~~~~~~~ [2/3] /usr/local/cuda/bin/nvcc -MD -MF cuda.cuda.o.d -ccbin /opt/conda/bin/x86_64-conda-linux-gnu-cc -DTORCH_EXTENSION_NAME=netvlad_ext_v1 -DTORCH_API_INCLUDE_EXTENSION_H -isystem /scratch/abaner/KernelBench/.venv/lib/python3.10/site-packages/torch/include -isystem /scratch/abaner/KernelBench/.venv/lib/python3.10/site-packages/torch/include/torch/csrc/api/include -isystem /usr/local/cuda/include -isystem /opt/conda/include/python3.10 -D__CUDA_NO_HALF_OPERATORS__ -D__CUDA_NO_HALF_CONVERSIONS__ -D__CUDA_NO_BFLOAT16_CONVERSIONS__ -D__CUDA_NO_HALF2_OPERATORS__ --expt-relaxed-constexpr -gencode=arch=compute_90,code=compute_90 -gencode=arch=compute_90,code=sm_90 --compiler-options '-fPIC' -O3 --use_fast_math -std=c++17 -c /scratch/abaner/KernelBench/runs/l3_default_gpt/original/gpt-5.5-priority/level_3_problem_46_cache/k_0f1519993d44/netvlad_ext_v1/cuda.cu -o cuda.cuda.o nvcc warning : incompatible redefinition for option 'compiler-bindir', the last value of this option was used ninja: build stopped: subcommand failed.
turn 2 · 1 tool call(s) · 16.1s turn · 16.1s LLM · 7,778 chars (click to expand)
turn 216.1s turn · 16.1s LLM · 0.0s tools
reasoning
**Considering function prototype** I'm thinking about whether I need to declare the function prototype for cpp_sources. It seems like loading `main.cpp` doesn't recognize cuda functions. Should I provide a cpp_sources declaration? Also, I’m noticing there isn't a pybind module available. My guess is that using cpp_sources with something like `torch::Tensor vlad_forward_cuda(...)` could potentially resolve the issue. I really want to make sure everything is set up correctly! **Considering mathematical approximations** I'm looking into the option of using `use_fast_math`, which alters the `expf` approximation. That might be okay for certain situations, but I need to consider if I really want to go that route. For now, I'll focus on compiling with the `cpp_sources` prototype. It’s all part of making sure everything runs smoothly. I want to ensure I make the right decisions moving forward, so it's worth evaluating all my options!
call → compile_kernel
callcompile_kernel { "kernel_code": "import math\nimport torch\nimport torch.nn as nn\nimport torch as th\nfrom torch.utils.cpp_extension import load_inline\n\n_cpp_src = r'''\n#include <torch/extension.h>\ntorch::Tensor vlad_forward_cuda(torch::Tensor x, torch::Tensor logits, torch::Tensor clusters2, int64_t B, int64_t N, int64_t D, int64_t K, int64_t C);\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 <vector>\n#include <math.h>\n#define D_TILE 16\n#define K_TILE 16\n__global__ void softmax_keep_kernel(const float* __restrict__ logits, float* __restrict__ probs, int rows, int C, int K) {\n int row = blockIdx.x; int tid = threadIdx.x;\n const float* in = logits + (long long)row * C;\n float m = -INFINITY;\n for (int c = tid; c < C; c += blockDim.x) m = fmaxf(m, in[c]);\n __shared__ float smem[64]; smem[tid] = m; __syncthreads();\n for (int s = blockDim.x >> 1; s > 0; s >>= 1) { if (tid < s) smem[tid] = fmaxf(smem[tid], smem[tid+s]); __syncthreads(); }\n m = smem[0]; float sum = 0.0f;\n for (int c = tid; c < C; c += blockDim.x) sum += expf(in[c] - m);\n smem[tid] = sum; __syncthreads();\n for (int s = blockDim.x >> 1; s > 0; s >>= 1) { if (tid < s) smem[tid] += smem[tid+s]; __syncthreads(); }\n float inv = 1.0f / smem[0];\n for (int c = tid; c < K; c += blockDim.x) probs[(long long)row * K + c] = expf(in[c] - m) * inv;\n}\n__global__ void raw_vlad_kernel(const float* __restrict__ x, const float* __restrict__ probs, const float* __restrict__ c2, float* __restrict__ raw, float* __restrict__ partial, int B, int N, int D, int K, int num_dtiles) {\n int tx = threadIdx.x, ty = threadIdx.y;\n int d = blockIdx.x * D_TILE + tx;\n int k = blockIdx.y * K_TILE + ty;\n int b = blockIdx.z;\n float val = 0.0f;\n if (d < D && k < K) {\n float acc = 0.0f, ps = 0.0f;\n const float* xb = x + (long long)b * N * D + d;\n const float* pb = probs + (long long)b * N * K + k;\n #pragma unroll 4\n for (int n = 0; n < N; ++n) { float p = pb[(long long)n * K]; ps += p; acc += p * xb[(long long)n * D]; }\n val = acc - ps * c2[d * K + k];\n raw[((long long)b * D + d) * K + k] = val;\n }\n __shared__ float ss[K_TILE][D_TILE]; ss[ty][tx] = val * val; __syncthreads();\n if (tx < 8) ss[ty][tx] += ss[ty][tx+8]; __syncthreads();\n if (tx < 4) ss[ty][tx] += ss[ty][tx+4]; __syncthreads();\n if (tx < 2) ss[ty][tx] += ss[ty][tx+2]; __syncthreads();\n if (tx == 0) { float s = ss[ty][0] + ss[ty][1]; if (k < K) partial[((long long)b * K + k) * num_dtiles + blockIdx.x] = s; }\n}\n__global__ void norm_kernel(const float* __restrict__ raw, const float* __restrict__ partial, float* __restrict__ out, int B, int D, int K, int num_dtiles) {\n int b = blockIdx.x, k = blockIdx.y, tid = threadIdx.x;\n float norm2 = 0.0f;\n for (int t = tid; t < num_dtiles; t += blockDim.x) norm2 += partial[((long long)b * K + k) * num_dtiles + t];\n __shared__ float sm[256]; sm[tid] = norm2; __syncthreads();\n for (int s = blockDim.x >> 1; s > 0; s >>= 1) { if (tid < s) sm[tid] += sm[tid+s]; __syncthreads(); }\n float inv = (sm[0] > 1.0e-24f) ? (rsqrtf(sm[0]) * rsqrtf((float)K)) : 0.0f;\n for (int d = tid; d < D; d += blockDim.x) { long long idx = ((long long)b * D + d) * K + k; out[idx] = raw[idx] * inv; }\n}\ntorch::Tensor vlad_forward_cuda(torch::Tensor x, torch::Tensor logits, torch::Tensor clusters2, int64_t B, int64_t N, int64_t D, int64_t K, int64_t C) {\n auto opts = x.options();\n auto probs = torch::empty({B * N, K}, opts);\n auto raw = torch::empty({B, D, K}, opts);\n int64_t num_dtiles = (D + D_TILE - 1) / D_TILE;\n auto partial = torch::empty({B, K, num_dtiles}, opts);\n auto out = torch::empty({B, D * K}, opts);\n cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n softmax_keep_kernel<<<(int)(B*N), 64, 0, stream>>>(logits.data_ptr<float>(), probs.data_ptr<float>(), (int)(B*N), (int)C, (int)K);\n dim3 block(D_TILE, K_TILE); dim3 grid((unsigned)num_dtiles, (unsigned)((K + K_TILE - 1)/K_TILE), (unsigned)B);\n raw_vlad_kernel<<<grid, block, 0, stream>>>(x.data_ptr<float>(), probs.data_ptr<float>(), clusters2.data_ptr<float>(), raw.data_ptr<float>(), partial.data_ptr<float>(), (int)B, (int)N, (int)D, (int)K, (int)num_dtiles);\n dim3 grid2((unsigned)B, (unsigned)K);\n norm_kernel<<<grid2, 256, 0, stream>>>(raw.data_ptr<float>(), partial.data_ptr<float>(), out.data_ptr<float>(), (int)B, (int)D, (int)K, (int)num_dtiles);\n return out;\n}\n'''\n_ext = load_inline(name='netvlad_ext_v2', cpp_sources=_cpp_src, cuda_sources=_cuda_src, functions=['vlad_forward_cuda'], extra_cuda_cflags=['-O3'], extra_cflags=['-O3'], verbose=False)\nclass ModelNew(nn.Module):\n def __init__(self, cluster_size, feature_size, ghost_clusters):\n super(ModelNew, self).__init__()\n self.feature_size = feature_size; self.cluster_size = cluster_size; self.ghost_clusters = ghost_clusters\n init_sc = (1 / math.sqrt(feature_size)); clusters = cluster_size + ghost_clusters\n self.clusters = nn.Parameter(init_sc * th.randn(feature_size, clusters))\n self.batch_norm = nn.BatchNorm1d(clusters)\n self.clusters2 = nn.Parameter(init_sc * th.randn(1, feature_size, cluster_size))\n self.out_dim = self.cluster_size * feature_size\n def forward(self, x, mask=None):\n B = x.size(0); N = x.size(1); D = self.feature_size; K = self.cluster_size; C = K + self.ghost_clusters\n xc = x.contiguous()\n assignment = th.matmul(xc.view(-1, D), self.clusters)\n assignment = self.batch_norm(assignment)\n return _ext.vlad_forward_cuda(xc, assignment.contiguous(), self.clusters2.contiguous(), B, N, D, K, C)\n" }
result ← compile_kernel (ok)
okcompile_kernel compile_kernel PASSED: kernel compiled without errors.
turn 3 · 1 tool call(s) · 15.6s turn · 15.6s LLM · 6,648 chars (click to expand)
turn 315.6s turn · 15.6s LLM · 0.0s tools
call → run_correctness
callrun_correctness { "kernel_code": "import math\nimport torch\nimport torch.nn as nn\nimport torch as th\nfrom torch.utils.cpp_extension import load_inline\n\n_cpp_src = r'''\n#include <torch/extension.h>\ntorch::Tensor vlad_forward_cuda(torch::Tensor x, torch::Tensor logits, torch::Tensor clusters2, int64_t B, int64_t N, int64_t D, int64_t K, int64_t C);\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 <math.h>\n#define D_TILE 16\n#define K_TILE 16\n__global__ void softmax_keep_kernel(const float* __restrict__ logits, float* __restrict__ probs, int rows, int C, int K) {\n int row = blockIdx.x; int tid = threadIdx.x;\n const float* in = logits + (long long)row * C;\n float m = -INFINITY;\n for (int c = tid; c < C; c += blockDim.x) m = fmaxf(m, in[c]);\n __shared__ float smem[64]; smem[tid] = m; __syncthreads();\n for (int s = blockDim.x >> 1; s > 0; s >>= 1) { if (tid < s) smem[tid] = fmaxf(smem[tid], smem[tid+s]); __syncthreads(); }\n m = smem[0]; float sum = 0.0f;\n for (int c = tid; c < C; c += blockDim.x) sum += expf(in[c] - m);\n smem[tid] = sum; __syncthreads();\n for (int s = blockDim.x >> 1; s > 0; s >>= 1) { if (tid < s) smem[tid] += smem[tid+s]; __syncthreads(); }\n float inv = 1.0f / smem[0];\n for (int c = tid; c < K; c += blockDim.x) probs[(long long)row * K + c] = expf(in[c] - m) * inv;\n}\n__global__ void raw_vlad_kernel(const float* __restrict__ x, const float* __restrict__ probs, const float* __restrict__ c2, float* __restrict__ raw, float* __restrict__ partial, int B, int N, int D, int K, int num_dtiles) {\n int tx = threadIdx.x, ty = threadIdx.y;\n int d = blockIdx.x * D_TILE + tx;\n int k = blockIdx.y * K_TILE + ty;\n int b = blockIdx.z;\n float val = 0.0f;\n if (d < D && k < K) {\n float acc = 0.0f, ps = 0.0f;\n const float* xb = x + (long long)b * N * D + d;\n const float* pb = probs + (long long)b * N * K + k;\n #pragma unroll 4\n for (int n = 0; n < N; ++n) { float p = pb[(long long)n * K]; ps += p; acc += p * xb[(long long)n * D]; }\n val = acc - ps * c2[d * K + k];\n raw[((long long)b * D + d) * K + k] = val;\n }\n __shared__ float ss[K_TILE][D_TILE]; ss[ty][tx] = val * val; __syncthreads();\n if (tx < 8) ss[ty][tx] += ss[ty][tx+8]; __syncthreads();\n if (tx < 4) ss[ty][tx] += ss[ty][tx+4]; __syncthreads();\n if (tx < 2) ss[ty][tx] += ss[ty][tx+2]; __syncthreads();\n if (tx == 0) { float s = ss[ty][0] + ss[ty][1]; if (k < K) partial[((long long)b * K + k) * num_dtiles + blockIdx.x] = s; }\n}\n__global__ void norm_kernel(const float* __restrict__ raw, const float* __restrict__ partial, float* __restrict__ out, int B, int D, int K, int num_dtiles) {\n int b = blockIdx.x, k = blockIdx.y, tid = threadIdx.x;\n float norm2 = 0.0f;\n for (int t = tid; t < num_dtiles; t += blockDim.x) norm2 += partial[((long long)b * K + k) * num_dtiles + t];\n __shared__ float sm[256]; sm[tid] = norm2; __syncthreads();\n for (int s = blockDim.x >> 1; s > 0; s >>= 1) { if (tid < s) sm[tid] += sm[tid+s]; __syncthreads(); }\n float inv = (sm[0] > 1.0e-24f) ? (rsqrtf(sm[0]) * rsqrtf((float)K)) : 0.0f;\n for (int d = tid; d < D; d += blockDim.x) { long long idx = ((long long)b * D + d) * K + k; out[idx] = raw[idx] * inv; }\n}\ntorch::Tensor vlad_forward_cuda(torch::Tensor x, torch::Tensor logits, torch::Tensor clusters2, int64_t B, int64_t N, int64_t D, int64_t K, int64_t C) {\n auto opts = x.options(); auto probs = torch::empty({B * N, K}, opts); auto raw = torch::empty({B, D, K}, opts);\n int64_t num_dtiles = (D + D_TILE - 1) / D_TILE; auto partial = torch::empty({B, K, num_dtiles}, opts); auto out = torch::empty({B, D * K}, opts);\n cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n softmax_keep_kernel<<<(int)(B*N), 64, 0, stream>>>(logits.data_ptr<float>(), probs.data_ptr<float>(), (int)(B*N), (int)C, (int)K);\n dim3 block(D_TILE, K_TILE); dim3 grid((unsigned)num_dtiles, (unsigned)((K + K_TILE - 1)/K_TILE), (unsigned)B);\n raw_vlad_kernel<<<grid, block, 0, stream>>>(x.data_ptr<float>(), probs.data_ptr<float>(), clusters2.data_ptr<float>(), raw.data_ptr<float>(), partial.data_ptr<float>(), (int)B, (int)N, (int)D, (int)K, (int)num_dtiles);\n dim3 grid2((unsigned)B, (unsigned)K); norm_kernel<<<grid2, 256, 0, stream>>>(raw.data_ptr<float>(), partial.data_ptr<float>(), out.data_ptr<float>(), (int)B, (int)D, (int)K, (int)num_dtiles);\n return out;\n}\n'''\n_ext = load_inline(name='netvlad_ext_v2c', cpp_sources=_cpp_src, cuda_sources=_cuda_src, functions=['vlad_forward_cuda'], extra_cuda_cflags=['-O3'], extra_cflags=['-O3'], verbose=False)\nclass ModelNew(nn.Module):\n def __init__(self, cluster_size, feature_size, ghost_clusters):\n super(ModelNew, self).__init__(); self.feature_size=feature_size; self.cluster_size=cluster_size; self.ghost_clusters=ghost_clusters\n init_sc=(1/math.sqrt(feature_size)); clusters=cluster_size+ghost_clusters\n self.clusters=nn.Parameter(init_sc*th.randn(feature_size,clusters)); self.batch_norm=nn.BatchNorm1d(clusters); self.clusters2=nn.Parameter(init_sc*th.randn(1,feature_size,cluster_size)); self.out_dim=self.cluster_size*feature_size\n def forward(self,x,mask=None):\n B=x.size(0); N=x.size(1); D=self.feature_size; K=self.cluster_size; C=K+self.ghost_clusters; xc=x.contiguous(); assignment=th.matmul(xc.view(-1,D),self.clusters); assignment=self.batch_norm(assignment); return _ext.vlad_forward_cuda(xc,assignment.contiguous(),self.clusters2.contiguous(),B,N,D,K,C)\n" }
result ← run_correctness (ok)
okrun_correctness run_correctness PASSED: (5 / 5) trials all matched the reference. Numerical precision: max_abs_err=5.59e-09 mean_abs_err=9.97e-10 max_rel_err=7.96e-07
turn 4 · 1 tool call(s) · 30.1s turn · 30.1s LLM · 6,632 chars (click to expand)
turn 430.1s turn · 30.1s LLM · 0.0s tools
reasoning
**Considering broadcast optimization** I'm thinking about broadcasting p within the warp, since currently, each of the 32 lanes loads the same address, which might be a uniform load. Maybe that could be optimized by L1 on NVIDIA? It seems like having each lane accumulate redundancy might not be necessary. I’m pondering the idea of having each warp work on a k,d tile with p broadcasted instead. It’s crucial to test for correctness and maybe compile to see the results.
call → run_correctness
callrun_correctness { "kernel_code": "import math\nimport torch\nimport torch.nn as nn\nimport torch as th\nfrom torch.utils.cpp_extension import load_inline\n_cpp_src=r'''\n#include <torch/extension.h>\ntorch::Tensor vlad_forward_cuda(torch::Tensor x, torch::Tensor logits, torch::Tensor clusters2, int64_t B, int64_t N, int64_t D, int64_t K, int64_t C);\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 <math.h>\n#define D_TILE 32\n#define K_TILE 8\n#define WARPS_PER_BLOCK 4\n__inline__ __device__ float warp_sum(float v){ for(int o=16;o>0;o>>=1) v += __shfl_down_sync(0xffffffff,v,o); return v; }\n__inline__ __device__ float warp_max(float v){ for(int o=16;o>0;o>>=1) v = fmaxf(v,__shfl_down_sync(0xffffffff,v,o)); return v; }\n__global__ void softmax_keep_kernel(const float* __restrict__ logits,float* __restrict__ probs,int rows,int C,int K){\n int tid=threadIdx.x; int lane=tid&31; int wid=tid>>5; int row=blockIdx.x*WARPS_PER_BLOCK+wid; if(row>=rows) return;\n const float* in=logits+(long long)row*C; float m=-INFINITY;\n for(int c=lane;c<C;c+=32) m=fmaxf(m,in[c]);\n m=warp_max(m); float s=0.f;\n for(int c=lane;c<C;c+=32) s+=expf(in[c]-m);\n s=warp_sum(s); float inv=1.f/s;\n for(int c=lane;c<K;c+=32) probs[(long long)row*K+c]=expf(in[c]-m)*inv;\n}\n__global__ void raw_vlad_kernel(const float* __restrict__ x,const float* __restrict__ probs,const float* __restrict__ c2,float* __restrict__ raw,float* __restrict__ partial,int B,int N,int D,int K,int num_dtiles){\n int tx=threadIdx.x, ty=threadIdx.y; int d=blockIdx.x*D_TILE+tx; int k=blockIdx.y*K_TILE+ty; int b=blockIdx.z; float val=0.f;\n if(d<D && k<K){ float acc=0.f, ps=0.f; const float* xb=x+(long long)b*N*D+d; const float* pb=probs+(long long)b*N*K+k;\n #pragma unroll 4\n for(int n=0;n<N;++n){ float p=pb[(long long)n*K]; ps+=p; acc+=p*xb[(long long)n*D]; }\n val=acc-ps*c2[d*K+k]; raw[((long long)b*D+d)*K+k]=val; }\n float ss=warp_sum(val*val); if(tx==0 && k<K) partial[((long long)b*K+k)*num_dtiles+blockIdx.x]=ss;\n}\n__global__ void norm_kernel(const float* __restrict__ raw,const float* __restrict__ partial,float* __restrict__ out,int B,int D,int K,int num_dtiles){\n int b=blockIdx.x,k=blockIdx.y,tid=threadIdx.x; float norm2=0.f; for(int t=tid;t<num_dtiles;t+=blockDim.x) norm2+=partial[((long long)b*K+k)*num_dtiles+t];\n __shared__ float sm[256]; sm[tid]=norm2; __syncthreads(); for(int s=128;s>0;s>>=1){ if(tid<s) sm[tid]+=sm[tid+s]; __syncthreads(); }\n float inv=(sm[0]>1.e-24f)?(rsqrtf(sm[0])*rsqrtf((float)K)):0.f; for(int d=tid;d<D;d+=blockDim.x){ long long idx=((long long)b*D+d)*K+k; out[idx]=raw[idx]*inv; }\n}\ntorch::Tensor vlad_forward_cuda(torch::Tensor x,torch::Tensor logits,torch::Tensor clusters2,int64_t B,int64_t N,int64_t D,int64_t K,int64_t C){\n auto opts=x.options(); auto probs=torch::empty({B*N,K},opts); auto raw=torch::empty({B,D,K},opts); int64_t nd=(D+D_TILE-1)/D_TILE; auto partial=torch::empty({B,K,nd},opts); auto out=torch::empty({B,D*K},opts); cudaStream_t stream=at::cuda::getCurrentCUDAStream();\n softmax_keep_kernel<<<(int)((B*N+WARPS_PER_BLOCK-1)/WARPS_PER_BLOCK), WARPS_PER_BLOCK*32,0,stream>>>(logits.data_ptr<float>(),probs.data_ptr<float>(),(int)(B*N),(int)C,(int)K);\n dim3 block(D_TILE,K_TILE); dim3 grid((unsigned)nd,(unsigned)((K+K_TILE-1)/K_TILE),(unsigned)B); raw_vlad_kernel<<<grid,block,0,stream>>>(x.data_ptr<float>(),probs.data_ptr<float>(),clusters2.data_ptr<float>(),raw.data_ptr<float>(),partial.data_ptr<float>(),(int)B,(int)N,(int)D,(int)K,(int)nd);\n dim3 grid2((unsigned)B,(unsigned)K); norm_kernel<<<grid2,256,0,stream>>>(raw.data_ptr<float>(),partial.data_ptr<float>(),out.data_ptr<float>(),(int)B,(int)D,(int)K,(int)nd); return out;\n}\n'''\n_ext=load_inline(name='netvlad_ext_v3',cpp_sources=_cpp_src,cuda_sources=_cuda_src,functions=['vlad_forward_cuda'],extra_cuda_cflags=['-O3'],extra_cflags=['-O3'],verbose=False)\nclass ModelNew(nn.Module):\n def __init__(self,cluster_size,feature_size,ghost_clusters):\n super(ModelNew,self).__init__(); self.feature_size=feature_size; self.cluster_size=cluster_size; self.ghost_clusters=ghost_clusters; init_sc=1/math.sqrt(feature_size); clusters=cluster_size+ghost_clusters; self.clusters=nn.Parameter(init_sc*th.randn(feature_size,clusters)); self.batch_norm=nn.BatchNorm1d(clusters); self.clusters2=nn.Parameter(init_sc*th.randn(1,feature_size,cluster_size)); self.out_dim=self.cluster_size*feature_size\n def forward(self,x,mask=None):\n B=x.size(0); N=x.size(1); D=self.feature_size; K=self.cluster_size; C=K+self.ghost_clusters; xc=x.contiguous(); assignment=th.matmul(xc.view(-1,D),self.clusters); assignment=self.batch_norm(assignment); return _ext.vlad_forward_cuda(xc,assignment.contiguous(),self.clusters2.contiguous(),B,N,D,K,C)\n" }
result ← run_correctness (fail)
failrun_correctness run_correctness FAILED: (0 / 5) trials did not all match. correctness_issue: Output mismatch max_difference: ['0.002939', '0.002943', '0.002883', '0.002991', '0.002754'] avg_difference: ['0.000250', '0.000250', '0.000250', '0.000250', '0.000249']
turn 511.9s turn · 11.9s LLM · 0.0s tools
call → run_correctness
callrun_correctness { "kernel_code": "import math\nimport torch\nimport torch.nn as nn\nimport torch as th\nfrom torch.utils.cpp_extension import load_inline\n_cpp_src=r'''\n#include <torch/extension.h>\ntorch::Tensor vlad_forward_cuda(torch::Tensor x, torch::Tensor logits, torch::Tensor clusters2, int64_t B, int64_t N, int64_t D, int64_t K, int64_t C);\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 <math.h>\n#define D_TILE 32\n#define K_TILE 8\n#define WARPS_PER_BLOCK 4\n__inline__ __device__ float warp_sum0(float v){ for(int o=16;o>0;o>>=1) v += __shfl_down_sync(0xffffffff,v,o); return v; }\n__inline__ __device__ float warp_max0(float v){ for(int o=16;o>0;o>>=1) v = fmaxf(v,__shfl_down_sync(0xffffffff,v,o)); return v; }\n__global__ void softmax_keep_kernel(const float* __restrict__ logits,float* __restrict__ probs,int rows,int C,int K){\n int tid=threadIdx.x; int lane=tid&31; int wid=tid>>5; int row=blockIdx.x*WARPS_PER_BLOCK+wid; if(row>=rows) return;\n const float* in=logits+(long long)row*C; float m=-INFINITY;\n for(int c=lane;c<C;c+=32) m=fmaxf(m,in[c]);\n m=warp_max0(m); m=__shfl_sync(0xffffffff,m,0);\n float s=0.f; for(int c=lane;c<C;c+=32) s+=expf(in[c]-m);\n s=warp_sum0(s); s=__shfl_sync(0xffffffff,s,0); float inv=1.f/s;\n for(int c=lane;c<K;c+=32) probs[(long long)row*K+c]=expf(in[c]-m)*inv;\n}\n__global__ void raw_vlad_kernel(const float* __restrict__ x,const float* __restrict__ probs,const float* __restrict__ c2,float* __restrict__ raw,float* __restrict__ partial,int B,int N,int D,int K,int num_dtiles){\n int tx=threadIdx.x, ty=threadIdx.y; int d=blockIdx.x*D_TILE+tx; int k=blockIdx.y*K_TILE+ty; int b=blockIdx.z; float val=0.f;\n if(d<D && k<K){ float acc=0.f, ps=0.f; const float* xb=x+(long long)b*N*D+d; const float* pb=probs+(long long)b*N*K+k;\n #pragma unroll 4\n for(int n=0;n<N;++n){ float p=pb[(long long)n*K]; ps+=p; acc+=p*xb[(long long)n*D]; }\n val=acc-ps*c2[d*K+k]; raw[((long long)b*D+d)*K+k]=val; }\n float ss=warp_sum0(val*val); if(tx==0 && k<K) partial[((long long)b*K+k)*num_dtiles+blockIdx.x]=ss;\n}\n__global__ void norm_kernel(const float* __restrict__ raw,const float* __restrict__ partial,float* __restrict__ out,int B,int D,int K,int num_dtiles){\n int b=blockIdx.x,k=blockIdx.y,tid=threadIdx.x; float norm2=0.f; for(int t=tid;t<num_dtiles;t+=blockDim.x) norm2+=partial[((long long)b*K+k)*num_dtiles+t];\n __shared__ float sm[256]; sm[tid]=norm2; __syncthreads(); for(int s=128;s>0;s>>=1){ if(tid<s) sm[tid]+=sm[tid+s]; __syncthreads(); }\n float inv=(sm[0]>1.e-24f)?(rsqrtf(sm[0])*rsqrtf((float)K)):0.f; for(int d=tid;d<D;d+=blockDim.x){ long long idx=((long long)b*D+d)*K+k; out[idx]=raw[idx]*inv; }\n}\ntorch::Tensor vlad_forward_cuda(torch::Tensor x,torch::Tensor logits,torch::Tensor clusters2,int64_t B,int64_t N,int64_t D,int64_t K,int64_t C){\n auto opts=x.options(); auto probs=torch::empty({B*N,K},opts); auto raw=torch::empty({B,D,K},opts); int64_t nd=(D+D_TILE-1)/D_TILE; auto partial=torch::empty({B,K,nd},opts); auto out=torch::empty({B,D*K},opts); cudaStream_t stream=at::cuda::getCurrentCUDAStream();\n softmax_keep_kernel<<<(int)((B*N+WARPS_PER_BLOCK-1)/WARPS_PER_BLOCK), WARPS_PER_BLOCK*32,0,stream>>>(logits.data_ptr<float>(),probs.data_ptr<float>(),(int)(B*N),(int)C,(int)K);\n dim3 block(D_TILE,K_TILE); dim3 grid((unsigned)nd,(unsigned)((K+K_TILE-1)/K_TILE),(unsigned)B); raw_vlad_kernel<<<grid,block,0,stream>>>(x.data_ptr<float>(),probs.data_ptr<float>(),clusters2.data_ptr<float>(),raw.data_ptr<float>(),partial.data_ptr<float>(),(int)B,(int)N,(int)D,(int)K,(int)nd);\n dim3 grid2((unsigned)B,(unsigned)K); norm_kernel<<<grid2,256,0,stream>>>(raw.data_ptr<float>(),partial.data_ptr<float>(),out.data_ptr<float>(),(int)B,(int)D,(int)K,(int)nd); return out;\n}\n'''\n_ext=load_inline(name='netvlad_ext_v3b',cpp_sources=_cpp_src,cuda_sources=_cuda_src,functions=['vlad_forward_cuda'],extra_cuda_cflags=['-O3'],extra_cflags=['-O3'],verbose=False)\nclass ModelNew(nn.Module):\n def __init__(self,cluster_size,feature_size,ghost_clusters):\n super(ModelNew,self).__init__(); self.feature_size=feature_size; self.cluster_size=cluster_size; self.ghost_clusters=ghost_clusters; init_sc=1/math.sqrt(feature_size); clusters=cluster_size+ghost_clusters; self.clusters=nn.Parameter(init_sc*th.randn(feature_size,clusters)); self.batch_norm=nn.BatchNorm1d(clusters); self.clusters2=nn.Parameter(init_sc*th.randn(1,feature_size,cluster_size)); self.out_dim=self.cluster_size*feature_size\n def forward(self,x,mask=None):\n B=x.size(0); N=x.size(1); D=self.feature_size; K=self.cluster_size; C=K+self.ghost_clusters; xc=x.contiguous(); assignment=th.matmul(xc.view(-1,D),self.clusters); assignment=self.batch_norm(assignment); return _ext.vlad_forward_cuda(xc,assignment.contiguous(),self.clusters2.contiguous(),B,N,D,K,C)\n" }
result ← run_correctness (ok)
okrun_correctness run_correctness PASSED: (5 / 5) trials all matched the reference. Numerical precision: max_abs_err=5.59e-09 mean_abs_err=9.97e-10 max_rel_err=7.57e-07
turn 6 · 1 tool call(s) · 24.4s turn · 24.4s LLM · 7,841 chars (click to expand)
turn 624.4s turn · 24.4s LLM · 0.0s tools
reasoning
**Evaluating performance options** I need to decide between v2 and v3 performance. It seems like v3 is likely better, but I noticed that v2 has a peculiar write pattern with two per warp and some repeated segments. V3 looks cleaner. I think I can optimize the norm kernel and consider using an alternate layout. To improve coalescing, I might compute some data in one kernel and apply it over a contiguous output. The current approach seems inefficient with a lot of memory scatter for high read and write operations. **Optimizing norm kernel** For norm_kernel2, I'm thinking about computing invs[B,K] with a grid of 32 or 64 threads and using 16 dtiles. I want to apply a linear kernel over the entire B*D*K range, making sure that the indexing is coalesced for efficient reads. This would require launching a kernel and allocating a small amount of space for invs, but it should speed up processes handling 134MB. The current setup uses a lot of threads and isn't coalesced, which isn't ideal. I believe this new method could enhance performance significantly. Let's implement it! **Considering raw data storage** I'm thinking we could store the raw data in a coalesced format with dimensions B, K, and D within the raw kernel. By fixing the k and d lanes in a contiguous warp, we might improve efficiency. Then, we could write out using a linear coalesced method for the B, D, K layout. This approach could help streamline kernel operations and enhance performance, so it's definitely worth exploring! Let's see how this plays out in practice.
call → run_correctness
callrun_correctness { "kernel_code": "import math\nimport torch\nimport torch.nn as nn\nimport torch as th\nfrom torch.utils.cpp_extension import load_inline\n_cpp_src=r'''\n#include <torch/extension.h>\ntorch::Tensor vlad_forward_cuda(torch::Tensor x, torch::Tensor logits, torch::Tensor clusters2, int64_t B, int64_t N, int64_t D, int64_t K, int64_t C);\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 <math.h>\n#define D_TILE 32\n#define K_TILE 8\n#define WARPS_PER_BLOCK 4\n__inline__ __device__ float warp_sum0(float v){ for(int o=16;o>0;o>>=1) v += __shfl_down_sync(0xffffffff,v,o); return v; }\n__inline__ __device__ float warp_max0(float v){ for(int o=16;o>0;o>>=1) v = fmaxf(v,__shfl_down_sync(0xffffffff,v,o)); return v; }\n__global__ void softmax_keep_kernel(const float* __restrict__ logits,float* __restrict__ probs,int rows,int C,int K){\n int tid=threadIdx.x; int lane=tid&31; int wid=tid>>5; int row=blockIdx.x*WARPS_PER_BLOCK+wid; if(row>=rows) return;\n const float* in=logits+(long long)row*C; float m=-INFINITY; for(int c=lane;c<C;c+=32) m=fmaxf(m,in[c]); m=warp_max0(m); m=__shfl_sync(0xffffffff,m,0);\n float s=0.f; for(int c=lane;c<C;c+=32) s+=expf(in[c]-m); s=warp_sum0(s); s=__shfl_sync(0xffffffff,s,0); float inv=1.f/s;\n if(lane<K) probs[(long long)row*K+lane]=expf(in[lane]-m)*inv;\n}\n__global__ void raw_vlad_kernel(const float* __restrict__ x,const float* __restrict__ probs,const float* __restrict__ c2,float* __restrict__ raw,float* __restrict__ partial,int B,int N,int D,int K,int num_dtiles){\n int tx=threadIdx.x, ty=threadIdx.y; int d=blockIdx.x*D_TILE+tx; int k=blockIdx.y*K_TILE+ty; int b=blockIdx.z; float val=0.f;\n if(d<D && k<K){ float acc=0.f, ps=0.f; const float* xb=x+(long long)b*N*D+d; const float* pb=probs+(long long)b*N*K+k;\n #pragma unroll 4\n for(int n=0;n<N;++n){ float p=pb[(long long)n*K]; ps+=p; acc+=p*xb[(long long)n*D]; }\n val=acc-ps*c2[d*K+k]; raw[((long long)b*D+d)*K+k]=val; }\n float ss=warp_sum0(val*val); if(tx==0 && k<K) partial[((long long)b*K+k)*num_dtiles+blockIdx.x]=ss;\n}\n__global__ void invnorm_kernel(const float* __restrict__ partial,float* __restrict__ invs,int B,int K,int num_dtiles){\n int tid=threadIdx.x; int k=blockIdx.x; int b=blockIdx.y; float s=0.f; if(tid<num_dtiles) s=partial[((long long)b*K+k)*num_dtiles+tid]; s=warp_sum0(s); if(tid==0) invs[(long long)b*K+k]=(s>1.e-24f)?(rsqrtf(s)*rsqrtf((float)K)):0.f;\n}\n__global__ void apply_kernel(const float* __restrict__ raw,const float* __restrict__ invs,float* __restrict__ out,long long total,int D,int K){\n long long idx=(long long)blockIdx.x*blockDim.x+threadIdx.x; if(idx>=total) return; int k=idx%K; long long bd=idx/K; int b=bd/D; out[idx]=raw[idx]*invs[(long long)b*K+k];\n}\ntorch::Tensor vlad_forward_cuda(torch::Tensor x,torch::Tensor logits,torch::Tensor clusters2,int64_t B,int64_t N,int64_t D,int64_t K,int64_t C){\n auto opts=x.options(); auto probs=torch::empty({B*N,K},opts); auto raw=torch::empty({B,D,K},opts); int64_t nd=(D+D_TILE-1)/D_TILE; auto partial=torch::empty({B,K,nd},opts); auto invs=torch::empty({B,K},opts); auto out=torch::empty({B,D*K},opts); cudaStream_t stream=at::cuda::getCurrentCUDAStream();\n softmax_keep_kernel<<<(int)((B*N+WARPS_PER_BLOCK-1)/WARPS_PER_BLOCK), WARPS_PER_BLOCK*32,0,stream>>>(logits.data_ptr<float>(),probs.data_ptr<float>(),(int)(B*N),(int)C,(int)K);\n dim3 block(D_TILE,K_TILE); dim3 grid((unsigned)nd,(unsigned)((K+K_TILE-1)/K_TILE),(unsigned)B); raw_vlad_kernel<<<grid,block,0,stream>>>(x.data_ptr<float>(),probs.data_ptr<float>(),clusters2.data_ptr<float>(),raw.data_ptr<float>(),partial.data_ptr<float>(),(int)B,(int)N,(int)D,(int)K,(int)nd);\n dim3 gridn((unsigned)K,(unsigned)B); invnorm_kernel<<<gridn,32,0,stream>>>(partial.data_ptr<float>(),invs.data_ptr<float>(),(int)B,(int)K,(int)nd);\n long long total=B*D*K; apply_kernel<<<(unsigned)((total+255)/256),256,0,stream>>>(raw.data_ptr<float>(),invs.data_ptr<float>(),out.data_ptr<float>(),total,(int)D,(int)K); return out;\n}\n'''\n_ext=load_inline(name='netvlad_ext_v4',cpp_sources=_cpp_src,cuda_sources=_cuda_src,functions=['vlad_forward_cuda'],extra_cuda_cflags=['-O3'],extra_cflags=['-O3'],verbose=False)\nclass ModelNew(nn.Module):\n def __init__(self,cluster_size,feature_size,ghost_clusters):\n super(ModelNew,self).__init__(); self.feature_size=feature_size; self.cluster_size=cluster_size; self.ghost_clusters=ghost_clusters; init_sc=1/math.sqrt(feature_size); clusters=cluster_size+ghost_clusters; self.clusters=nn.Parameter(init_sc*th.randn(feature_size,clusters)); self.batch_norm=nn.BatchNorm1d(clusters); self.clusters2=nn.Parameter(init_sc*th.randn(1,feature_size,cluster_size)); self.out_dim=self.cluster_size*feature_size\n def forward(self,x,mask=None):\n B=x.size(0); N=x.size(1); D=self.feature_size; K=self.cluster_size; C=K+self.ghost_clusters; xc=x.contiguous(); assignment=th.matmul(xc.view(-1,D),self.clusters); assignment=self.batch_norm(assignment); return _ext.vlad_forward_cuda(xc,assignment.contiguous(),self.clusters2.contiguous(),B,N,D,K,C)\n" }
result ← run_correctness (ok)
okrun_correctness run_correctness PASSED: (5 / 5) trials all matched the reference. Numerical precision: max_abs_err=5.59e-09 mean_abs_err=9.97e-10 max_rel_err=7.57e-07
turn 7 · 1 tool call(s) · 11.8s turn · 11.8s LLM · 6,673 chars (click to expand)
turn 711.8s turn · 11.8s LLM · 0.0s tools
reasoning
**Considering normalization in PyTorch** I need to finalize version 4, focusing on the exact normalization process. I realize we need to divide by sqrt(K), but in cases where there's a raw zero cluster, the PyTorch final norm won’t work as expected. I’m thinking it might be better to compute the final norm precisely using invnorm. Each cluster should be non-zero, so summing active elements should be fine. I’m weighing whether static flags will affect the use of torch operations, but I decide to keep it static.
call → static_check
callstatic_check { "kernel_code": "import math\nimport torch\nimport torch.nn as nn\nimport torch as th\nfrom torch.utils.cpp_extension import load_inline\n_cpp_src=r'''\n#include <torch/extension.h>\ntorch::Tensor vlad_forward_cuda(torch::Tensor x, torch::Tensor logits, torch::Tensor clusters2, int64_t B, int64_t N, int64_t D, int64_t K, int64_t C);\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 <math.h>\n#define D_TILE 32\n#define K_TILE 8\n#define WARPS_PER_BLOCK 4\n__inline__ __device__ float warp_sum0(float v){ for(int o=16;o>0;o>>=1) v += __shfl_down_sync(0xffffffff,v,o); return v; }\n__inline__ __device__ float warp_max0(float v){ for(int o=16;o>0;o>>=1) v = fmaxf(v,__shfl_down_sync(0xffffffff,v,o)); return v; }\n__global__ void softmax_keep_kernel(const float* __restrict__ logits,float* __restrict__ probs,int rows,int C,int K){\n int tid=threadIdx.x; int lane=tid&31; int wid=tid>>5; int row=blockIdx.x*WARPS_PER_BLOCK+wid; if(row>=rows) return;\n const float* in=logits+(long long)row*C; float m=-INFINITY; for(int c=lane;c<C;c+=32) m=fmaxf(m,in[c]); m=warp_max0(m); m=__shfl_sync(0xffffffff,m,0);\n float s=0.f; for(int c=lane;c<C;c+=32) s+=expf(in[c]-m); s=warp_sum0(s); s=__shfl_sync(0xffffffff,s,0); float inv=1.f/s;\n if(lane<K) probs[(long long)row*K+lane]=expf(in[lane]-m)*inv;\n}\n__global__ void raw_vlad_kernel(const float* __restrict__ x,const float* __restrict__ probs,const float* __restrict__ c2,float* __restrict__ raw,float* __restrict__ partial,int B,int N,int D,int K,int num_dtiles){\n int tx=threadIdx.x, ty=threadIdx.y; int d=blockIdx.x*D_TILE+tx; int k=blockIdx.y*K_TILE+ty; int b=blockIdx.z; float val=0.f;\n if(d<D && k<K){ float acc=0.f, ps=0.f; const float* xb=x+(long long)b*N*D+d; const float* pb=probs+(long long)b*N*K+k;\n #pragma unroll 4\n for(int n=0;n<N;++n){ float p=pb[(long long)n*K]; ps+=p; acc+=p*xb[(long long)n*D]; }\n val=acc-ps*c2[d*K+k]; raw[((long long)b*D+d)*K+k]=val; }\n float ss=warp_sum0(val*val); if(tx==0 && k<K) partial[((long long)b*K+k)*num_dtiles+blockIdx.x]=ss;\n}\n__global__ void invnorm_kernel(const float* __restrict__ partial,float* __restrict__ invs,int B,int K,int num_dtiles){\n int tid=threadIdx.x; int k=blockIdx.x; int b=blockIdx.y; float s=0.f; if(tid<num_dtiles) s=partial[((long long)b*K+k)*num_dtiles+tid]; s=warp_sum0(s); if(tid==0) invs[(long long)b*K+k]=(s>1.e-24f)?(rsqrtf(s)*rsqrtf((float)K)):0.f;\n}\n__global__ void apply_kernel(const float* __restrict__ raw,const float* __restrict__ invs,float* __restrict__ out,long long total,int D,int K){\n long long idx=(long long)blockIdx.x*blockDim.x+threadIdx.x; if(idx>=total) return; int k=idx%K; long long bd=idx/K; int b=bd/D; out[idx]=raw[idx]*invs[(long long)b*K+k];\n}\ntorch::Tensor vlad_forward_cuda(torch::Tensor x,torch::Tensor logits,torch::Tensor clusters2,int64_t B,int64_t N,int64_t D,int64_t K,int64_t C){\n auto opts=x.options(); auto probs=torch::empty({B*N,K},opts); auto raw=torch::empty({B,D,K},opts); int64_t nd=(D+D_TILE-1)/D_TILE; auto partial=torch::empty({B,K,nd},opts); auto invs=torch::empty({B,K},opts); auto out=torch::empty({B,D*K},opts); cudaStream_t stream=at::cuda::getCurrentCUDAStream();\n softmax_keep_kernel<<<(int)((B*N+WARPS_PER_BLOCK-1)/WARPS_PER_BLOCK), WARPS_PER_BLOCK*32,0,stream>>>(logits.data_ptr<float>(),probs.data_ptr<float>(),(int)(B*N),(int)C,(int)K);\n dim3 block(D_TILE,K_TILE); dim3 grid((unsigned)nd,(unsigned)((K+K_TILE-1)/K_TILE),(unsigned)B); raw_vlad_kernel<<<grid,block,0,stream>>>(x.data_ptr<float>(),probs.data_ptr<float>(),clusters2.data_ptr<float>(),raw.data_ptr<float>(),partial.data_ptr<float>(),(int)B,(int)N,(int)D,(int)K,(int)nd);\n dim3 gridn((unsigned)K,(unsigned)B); invnorm_kernel<<<gridn,32,0,stream>>>(partial.data_ptr<float>(),invs.data_ptr<float>(),(int)B,(int)K,(int)nd);\n long long total=B*D*K; apply_kernel<<<(unsigned)((total+255)/256),256,0,stream>>>(raw.data_ptr<float>(),invs.data_ptr<float>(),out.data_ptr<float>(),total,(int)D,(int)K); return out;\n}\n'''\n_ext=load_inline(name='netvlad_ext_final',cpp_sources=_cpp_src,cuda_sources=_cuda_src,functions=['vlad_forward_cuda'],extra_cuda_cflags=['-O3'],extra_cflags=['-O3'],verbose=False)\nclass ModelNew(nn.Module):\n def __init__(self,cluster_size,feature_size,ghost_clusters):\n super(ModelNew,self).__init__(); self.feature_size=feature_size; self.cluster_size=cluster_size; self.ghost_clusters=ghost_clusters; init_sc=1/math.sqrt(feature_size); clusters=cluster_size+ghost_clusters; self.clusters=nn.Parameter(init_sc*th.randn(feature_size,clusters)); self.batch_norm=nn.BatchNorm1d(clusters); self.clusters2=nn.Parameter(init_sc*th.randn(1,feature_size,cluster_size)); self.out_dim=self.cluster_size*feature_size\n def forward(self,x,mask=None):\n B=x.size(0); N=x.size(1); D=self.feature_size; K=self.cluster_size; C=K+self.ghost_clusters; xc=x.contiguous(); assignment=th.matmul(xc.view(-1,D),self.clusters); assignment=self.batch_norm(assignment); return _ext.vlad_forward_cuda(xc,assignment.contiguous(),self.clusters2.contiguous(),B,N,D,K,C)\n" }
result ← static_check (ok)
okstatic_check static_check PASSED: no violations or warnings detected.
turn 8 · FINAL · 1 tool call(s) · 13.4s turn · 13.4s LLM · 8,499 chars (click to expand)
turn 813.4s turn · 13.4s LLM · 0.0s tools · FINAL
reasoning
**Finalizing submission** I need to submit, but before I do, it seems wise to get the timing right by submitting only what’s necessary. I want to make sure there are no code typos in my final response. It looks like I’ll be using the submit_kernel tool for this, rather than just a textual submission. I want to be careful and methodical to ensure everything goes smoothly in the final step!
call → submit_kernel
callsubmit_kernel { "kernel_code": "import math\nimport torch\nimport torch.nn as nn\nimport torch as th\nfrom torch.utils.cpp_extension import load_inline\n\n_cpp_src = r'''\n#include <torch/extension.h>\ntorch::Tensor vlad_forward_cuda(torch::Tensor x, torch::Tensor logits, torch::Tensor clusters2, int64_t B, int64_t N, int64_t D, int64_t K, int64_t C);\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 <math.h>\n\n#define D_TILE 32\n#define K_TILE 8\n#define WARPS_PER_BLOCK 4\n\n__inline__ __device__ float warp_sum0(float v) {\n for (int o = 16; o > 0; o >>= 1) v += __shfl_down_sync(0xffffffff, v, o);\n return v;\n}\n\n__inline__ __device__ float warp_max0(float v) {\n for (int o = 16; o > 0; o >>= 1) v = fmaxf(v, __shfl_down_sync(0xffffffff, v, o));\n return v;\n}\n\n__global__ void softmax_keep_kernel(const float* __restrict__ logits,\n float* __restrict__ probs,\n int rows, int C, int K) {\n int tid = threadIdx.x;\n int lane = tid & 31;\n int wid = tid >> 5;\n int row = blockIdx.x * WARPS_PER_BLOCK + wid;\n if (row >= rows) return;\n\n const float* in = logits + (long long)row * C;\n float m = -INFINITY;\n for (int c = lane; c < C; c += 32) m = fmaxf(m, in[c]);\n m = warp_max0(m);\n m = __shfl_sync(0xffffffff, m, 0);\n\n float s = 0.0f;\n for (int c = lane; c < C; c += 32) s += expf(in[c] - m);\n s = warp_sum0(s);\n s = __shfl_sync(0xffffffff, s, 0);\n float inv = 1.0f / s;\n\n if (lane < K) probs[(long long)row * K + lane] = expf(in[lane] - m) * inv;\n}\n\n__global__ void raw_vlad_kernel(const float* __restrict__ x,\n const float* __restrict__ probs,\n const float* __restrict__ c2,\n float* __restrict__ raw,\n float* __restrict__ partial,\n int B, int N, int D, int K, int num_dtiles) {\n int tx = threadIdx.x;\n int ty = threadIdx.y;\n int d = blockIdx.x * D_TILE + tx;\n int k = blockIdx.y * K_TILE + ty;\n int b = blockIdx.z;\n float val = 0.0f;\n\n if (d < D && k < K) {\n float acc = 0.0f;\n float ps = 0.0f;\n const float* xb = x + (long long)b * N * D + d;\n const float* pb = probs + (long long)b * N * K + k;\n #pragma unroll 4\n for (int n = 0; n < N; ++n) {\n float p = pb[(long long)n * K];\n ps += p;\n acc += p * xb[(long long)n * D];\n }\n val = acc - ps * c2[d * K + k];\n raw[((long long)b * D + d) * K + k] = val;\n }\n\n float ss = warp_sum0(val * val);\n if (tx == 0 && k < K) {\n partial[((long long)b * K + k) * num_dtiles + blockIdx.x] = ss;\n }\n}\n\n__global__ void invnorm_kernel(const float* __restrict__ partial,\n float* __restrict__ invs,\n int B, int K, int num_dtiles) {\n int tid = threadIdx.x;\n int k = blockIdx.x;\n int b = blockIdx.y;\n float s = 0.0f;\n if (tid < num_dtiles) s = partial[((long long)b * K + k) * num_dtiles + tid];\n s = warp_sum0(s);\n if (tid == 0) {\n invs[(long long)b * K + k] = (s > 1.0e-24f) ? (rsqrtf(s) * rsqrtf((float)K)) : 0.0f;\n }\n}\n\n__global__ void apply_kernel(const float* __restrict__ raw,\n const float* __restrict__ invs,\n float* __restrict__ out,\n long long total, int D, int K) {\n long long idx = (long long)blockIdx.x * blockDim.x + threadIdx.x;\n if (idx >= total) return;\n int k = idx % K;\n long long bd = idx / K;\n int b = bd / D;\n out[idx] = raw[idx] * invs[(long long)b * K + k];\n}\n\ntorch::Tensor vlad_forward_cuda(torch::Tensor x, torch::Tensor logits, torch::Tensor clusters2,\n int64_t B, int64_t N, int64_t D, int64_t K, int64_t C) {\n auto opts = x.options();\n auto probs = torch::empty({B * N, K}, opts);\n auto raw = torch::empty({B, D, K}, opts);\n int64_t nd = (D + D_TILE - 1) / D_TILE;\n auto partial = torch::empty({B, K, nd}, opts);\n auto invs = torch::empty({B, K}, opts);\n auto out = torch::empty({B, D * K}, opts);\n\n cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n softmax_keep_kernel<<<(int)((B * N + WARPS_PER_BLOCK - 1) / WARPS_PER_BLOCK),\n WARPS_PER_BLOCK * 32, 0, stream>>>(\n logits.data_ptr<float>(), probs.data_ptr<float>(), (int)(B * N), (int)C, (int)K);\n\n dim3 block(D_TILE, K_TILE);\n dim3 grid((unsigned)nd, (unsigned)((K + K_TILE - 1) / K_TILE), (unsigned)B);\n raw_vlad_kernel<<<grid, block, 0, stream>>>(\n x.data_ptr<float>(), probs.data_ptr<float>(), clusters2.data_ptr<float>(),\n raw.data_ptr<float>(), partial.data_ptr<float>(), (int)B, (int)N, (int)D, (int)K, (int)nd);\n\n dim3 gridn((unsigned)K, (unsigned)B);\n invnorm_kernel<<<gridn, 32, 0, stream>>>(partial.data_ptr<float>(), invs.data_ptr<float>(), (int)B, (int)K, (int)nd);\n\n long long total = B * D * K;\n apply_kernel<<<(unsigned)((total + 255) / 256), 256, 0, stream>>>(\n raw.data_ptr<float>(), invs.data_ptr<float>(), out.data_ptr<float>(), total, (int)D, (int)K);\n return out;\n}\n'''\n\n_ext = load_inline(\n name='netvlad_ext_final',\n cpp_sources=_cpp_src,\n cuda_sources=_cuda_src,\n functions=['vlad_forward_cuda'],\n extra_cuda_cflags=['-O3'],\n extra_cflags=['-O3'],\n verbose=False,\n)\n\nclass ModelNew(nn.Module):\n def __init__(self, cluster_size, feature_size, ghost_clusters):\n super(ModelNew, self).__init__()\n self.feature_size = feature_size\n self.cluster_size = cluster_size\n self.ghost_clusters = ghost_clusters\n\n init_sc = (1 / math.sqrt(feature_size))\n clusters = cluster_size + ghost_clusters\n self.clusters = nn.Parameter(init_sc * th.randn(feature_size, clusters))\n self.batch_norm = nn.BatchNorm1d(clusters)\n self.clusters2 = nn.Parameter(init_sc * th.randn(1, feature_size, cluster_size))\n self.out_dim = self.cluster_size * feature_size\n\n def forward(self, x, mask=None):\n B = x.size(0)\n N = x.size(1)\n D = self.feature_size\n K = self.cluster_size\n C = K + self.ghost_clusters\n xc = x.contiguous()\n assignment = th.matmul(xc.view(-1, D), self.clusters)\n assignment = self.batch_norm(assignment)\n return _ext.vlad_forward_cuda(xc, assignment.contiguous(), self.clusters2.contiguous(), B, N, D, K, C)\n" }
result ← submit_kernel (ok)
oksubmit_kernel submit_kernel PASSED: (5 / 5) correctness trials all passed. Kernel runtime: 2.52 μs Runtime stats: mean=2.52μs median=?μs std=0.60μs Numerical precision: max_abs_err=5.59e-09 mean_abs_err=9.97e-10 max_rel_err=7.57e-07 Memory: 756.57 MB peak (ref: 855.82 MB, ratio: 0.884x) Kernel launches: 22 (ref: 59, fusion ratio: 2.6818) Energy: 254.34 mJ/run (ref: 1016.8 mJ, ratio: 3.9978x) SOL score: 1.0
final submitted kernel (182 lines, 6,588 chars) — full source, untruncated
import math
import torch
import torch.nn as nn
import torch as th
from torch.utils.cpp_extension import load_inline

_cpp_src = r'''
#include <torch/extension.h>
torch::Tensor vlad_forward_cuda(torch::Tensor x, torch::Tensor logits, torch::Tensor clusters2, int64_t B, int64_t N, int64_t D, int64_t K, int64_t C);
'''

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

#define D_TILE 32
#define K_TILE 8
#define WARPS_PER_BLOCK 4

__inline__ __device__ float warp_sum0(float v) {
    for (int o = 16; o > 0; o >>= 1) v += __shfl_down_sync(0xffffffff, v, o);
    return v;
}

__inline__ __device__ float warp_max0(float v) {
    for (int o = 16; o > 0; o >>= 1) v = fmaxf(v, __shfl_down_sync(0xffffffff, v, o));
    return v;
}

__global__ void softmax_keep_kernel(const float* __restrict__ logits,
                                    float* __restrict__ probs,
                                    int rows, int C, int K) {
    int tid = threadIdx.x;
    int lane = tid & 31;
    int wid = tid >> 5;
    int row = blockIdx.x * WARPS_PER_BLOCK + wid;
    if (row >= rows) return;

    const float* in = logits + (long long)row * C;
    float m = -INFINITY;
    for (int c = lane; c < C; c += 32) m = fmaxf(m, in[c]);
    m = warp_max0(m);
    m = __shfl_sync(0xffffffff, m, 0);

    float s = 0.0f;
    for (int c = lane; c < C; c += 32) s += expf(in[c] - m);
    s = warp_sum0(s);
    s = __shfl_sync(0xffffffff, s, 0);
    float inv = 1.0f / s;

    if (lane < K) probs[(long long)row * K + lane] = expf(in[lane] - m) * inv;
}

__global__ void raw_vlad_kernel(const float* __restrict__ x,
                                const float* __restrict__ probs,
                                const float* __restrict__ c2,
                                float* __restrict__ raw,
                                float* __restrict__ partial,
                                int B, int N, int D, int K, int num_dtiles) {
    int tx = threadIdx.x;
    int ty = threadIdx.y;
    int d = blockIdx.x * D_TILE + tx;
    int k = blockIdx.y * K_TILE + ty;
    int b = blockIdx.z;
    float val = 0.0f;

    if (d < D && k < K) {
        float acc = 0.0f;
        float ps = 0.0f;
        const float* xb = x + (long long)b * N * D + d;
        const float* pb = probs + (long long)b * N * K + k;
        #pragma unroll 4
        for (int n = 0; n < N; ++n) {
            float p = pb[(long long)n * K];
            ps += p;
            acc += p * xb[(long long)n * D];
        }
        val = acc - ps * c2[d * K + k];
        raw[((long long)b * D + d) * K + k] = val;
    }

    float ss = warp_sum0(val * val);
    if (tx == 0 && k < K) {
        partial[((long long)b * K + k) * num_dtiles + blockIdx.x] = ss;
    }
}

__global__ void invnorm_kernel(const float* __restrict__ partial,
                               float* __restrict__ invs,
                               int B, int K, int num_dtiles) {
    int tid = threadIdx.x;
    int k = blockIdx.x;
    int b = blockIdx.y;
    float s = 0.0f;
    if (tid < num_dtiles) s = partial[((long long)b * K + k) * num_dtiles + tid];
    s = warp_sum0(s);
    if (tid == 0) {
        invs[(long long)b * K + k] = (s > 1.0e-24f) ? (rsqrtf(s) * rsqrtf((float)K)) : 0.0f;
    }
}

__global__ void apply_kernel(const float* __restrict__ raw,
                             const float* __restrict__ invs,
                             float* __restrict__ out,
                             long long total, int D, int K) {
    long long idx = (long long)blockIdx.x * blockDim.x + threadIdx.x;
    if (idx >= total) return;
    int k = idx % K;
    long long bd = idx / K;
    int b = bd / D;
    out[idx] = raw[idx] * invs[(long long)b * K + k];
}

torch::Tensor vlad_forward_cuda(torch::Tensor x, torch::Tensor logits, torch::Tensor clusters2,
                                int64_t B, int64_t N, int64_t D, int64_t K, int64_t C) {
    auto opts = x.options();
    auto probs = torch::empty({B * N, K}, opts);
    auto raw = torch::empty({B, D, K}, opts);
    int64_t nd = (D + D_TILE - 1) / D_TILE;
    auto partial = torch::empty({B, K, nd}, opts);
    auto invs = torch::empty({B, K}, opts);
    auto out = torch::empty({B, D * K}, opts);

    cudaStream_t stream = at::cuda::getCurrentCUDAStream();
    softmax_keep_kernel<<<(int)((B * N + WARPS_PER_BLOCK - 1) / WARPS_PER_BLOCK),
                          WARPS_PER_BLOCK * 32, 0, stream>>>(
        logits.data_ptr<float>(), probs.data_ptr<float>(), (int)(B * N), (int)C, (int)K);

    dim3 block(D_TILE, K_TILE);
    dim3 grid((unsigned)nd, (unsigned)((K + K_TILE - 1) / K_TILE), (unsigned)B);
    raw_vlad_kernel<<<grid, block, 0, stream>>>(
        x.data_ptr<float>(), probs.data_ptr<float>(), clusters2.data_ptr<float>(),
        raw.data_ptr<float>(), partial.data_ptr<float>(), (int)B, (int)N, (int)D, (int)K, (int)nd);

    dim3 gridn((unsigned)K, (unsigned)B);
    invnorm_kernel<<<gridn, 32, 0, stream>>>(partial.data_ptr<float>(), invs.data_ptr<float>(), (int)B, (int)K, (int)nd);

    long long total = B * D * K;
    apply_kernel<<<(unsigned)((total + 255) / 256), 256, 0, stream>>>(
        raw.data_ptr<float>(), invs.data_ptr<float>(), out.data_ptr<float>(), total, (int)D, (int)K);
    return out;
}
'''

_ext = load_inline(
    name='netvlad_ext_final',
    cpp_sources=_cpp_src,
    cuda_sources=_cuda_src,
    functions=['vlad_forward_cuda'],
    extra_cuda_cflags=['-O3'],
    extra_cflags=['-O3'],
    verbose=False,
)

class ModelNew(nn.Module):
    def __init__(self, cluster_size, feature_size, ghost_clusters):
        super(ModelNew, self).__init__()
        self.feature_size = feature_size
        self.cluster_size = cluster_size
        self.ghost_clusters = ghost_clusters

        init_sc = (1 / math.sqrt(feature_size))
        clusters = cluster_size + ghost_clusters
        self.clusters = nn.Parameter(init_sc * th.randn(feature_size, clusters))
        self.batch_norm = nn.BatchNorm1d(clusters)
        self.clusters2 = nn.Parameter(init_sc * th.randn(1, feature_size, cluster_size))
        self.out_dim = self.cluster_size * feature_size

    def forward(self, x, mask=None):
        B = x.size(0)
        N = x.size(1)
        D = self.feature_size
        K = self.cluster_size
        C = K + self.ghost_clusters
        xc = x.contiguous()
        assignment = th.matmul(xc.view(-1, D), self.clusters)
        assignment = self.batch_norm(assignment)
        return _ext.vlad_forward_cuda(xc, assignment.contiguous(), self.clusters2.contiguous(), B, N, D, K, C)