call → submit_kernel
callsubmit_kernel
{
"kernel_code": "import math\nimport torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\n_cpp_src = r'''\n#include <torch/extension.h>\n\ntorch::Tensor netvlad_cuda(torch::Tensor logits,\n torch::Tensor x,\n torch::Tensor clusters2,\n torch::Tensor bn_weight,\n torch::Tensor bn_bias,\n torch::Tensor running_mean,\n torch::Tensor running_var,\n bool training,\n double momentum,\n double eps,\n int64_t cluster_size);\n\ntorch::Tensor netvlad_forward(torch::Tensor logits,\n torch::Tensor x,\n torch::Tensor clusters2,\n torch::Tensor bn_weight,\n torch::Tensor bn_bias,\n torch::Tensor running_mean,\n torch::Tensor running_var,\n bool training,\n double momentum,\n double eps,\n int64_t cluster_size) {\n return netvlad_cuda(logits, x, clusters2, bn_weight, bn_bias, running_mean, running_var,\n training, momentum, eps, cluster_size);\n}\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__global__ void bn_stats_kernel(const float* __restrict__ logits,\n float* __restrict__ mean,\n float* __restrict__ invstd,\n float* __restrict__ running_mean,\n float* __restrict__ running_var,\n int M, int C, float momentum, float eps) {\n int c = blockIdx.x;\n __shared__ double ssum[256];\n __shared__ double ssq[256];\n double sum = 0.0;\n double sq = 0.0;\n for (int m = threadIdx.x; m < M; m += blockDim.x) {\n float v = logits[m * C + c];\n sum += (double)v;\n sq += (double)v * (double)v;\n }\n ssum[threadIdx.x] = sum;\n ssq[threadIdx.x] = sq;\n __syncthreads();\n for (int stride = blockDim.x >> 1; stride > 0; stride >>= 1) {\n if (threadIdx.x < stride) {\n ssum[threadIdx.x] += ssum[threadIdx.x + stride];\n ssq[threadIdx.x] += ssq[threadIdx.x + stride];\n }\n __syncthreads();\n }\n if (threadIdx.x == 0) {\n double mu = ssum[0] / (double)M;\n double var = ssq[0] / (double)M - mu * mu;\n if (var < 0.0) var = 0.0;\n mean[c] = (float)mu;\n invstd[c] = rsqrtf((float)var + eps);\n if (running_mean != nullptr) {\n running_mean[c] = running_mean[c] * (1.0f - momentum) + ((float)mu) * momentum;\n float unbiased = (M > 1) ? ((float)var * ((float)M / (float)(M - 1))) : (float)var;\n running_var[c] = running_var[c] * (1.0f - momentum) + unbiased * momentum;\n }\n }\n}\n\n__global__ void invstd_eval_kernel(const float* __restrict__ running_var,\n float* __restrict__ invstd,\n int C, float eps) {\n int c = blockIdx.x * blockDim.x + threadIdx.x;\n if (c < C) invstd[c] = rsqrtf(running_var[c] + eps);\n}\n\n__global__ void bn_softmax_kernel(const float* __restrict__ logits,\n const float* __restrict__ mean,\n const float* __restrict__ invstd,\n const float* __restrict__ weight,\n const float* __restrict__ bias,\n float* __restrict__ probs,\n int M, int C, int K) {\n int row = blockIdx.x * blockDim.x + threadIdx.x;\n if (row >= M) return;\n float vals[128];\n float mx = -3.402823466e38f;\n int base = row * C;\n #pragma unroll\n for (int c = 0; c < 128; ++c) {\n if (c < C) {\n float y = (logits[base + c] - mean[c]) * invstd[c];\n y = y * weight[c] + bias[c];\n vals[c] = y;\n mx = fmaxf(mx, y);\n }\n }\n float denom = 0.0f;\n #pragma unroll\n for (int c = 0; c < 128; ++c) {\n if (c < C) {\n float e = expf(vals[c] - mx);\n vals[c] = e;\n denom += e;\n }\n }\n float rden = 1.0f / denom;\n int obase = row * K;\n #pragma unroll\n for (int c = 0; c < 128; ++c) {\n if (c < K) probs[obase + c] = vals[c] * rden;\n }\n}\n\n__global__ void vlad_norm_kernel(const float* __restrict__ probs,\n const float* __restrict__ x,\n const float* __restrict__ clusters2,\n float* __restrict__ out,\n int B, int N, int D, int K) {\n int b = blockIdx.x;\n int k = blockIdx.y;\n int tid = threadIdx.x;\n __shared__ float ps[128];\n __shared__ float red[256];\n if (tid < N) ps[tid] = probs[(b * N + tid) * K + k];\n __syncthreads();\n\n float v0 = 0.0f, v1 = 0.0f;\n int d0 = tid;\n int d1 = tid + 256;\n if (d0 < D) {\n float c2 = clusters2[d0 * K + k];\n float s = 0.0f;\n #pragma unroll 4\n for (int n = 0; n < 100; ++n) {\n if (n < N) s += ps[n] * (x[(b * N + n) * D + d0] - c2);\n }\n v0 = s;\n }\n if (d1 < D) {\n float c2 = clusters2[d1 * K + k];\n float s = 0.0f;\n #pragma unroll 4\n for (int n = 0; n < 100; ++n) {\n if (n < N) s += ps[n] * (x[(b * N + n) * D + d1] - c2);\n }\n v1 = s;\n }\n float ss = v0 * v0 + v1 * v1;\n red[tid] = ss;\n __syncthreads();\n for (int stride = 128; stride > 0; stride >>= 1) {\n if (tid < stride) red[tid] += red[tid + stride];\n __syncthreads();\n }\n float scale = rsqrtf(red[0] + 1.0e-12f) * rsqrtf((float)K);\n if (d0 < D) out[b * (D * K) + d0 * K + k] = v0 * scale;\n if (d1 < D) out[b * (D * K) + d1 * K + k] = v1 * scale;\n}\n\ntorch::Tensor netvlad_cuda(torch::Tensor logits,\n torch::Tensor x,\n torch::Tensor clusters2,\n torch::Tensor bn_weight,\n torch::Tensor bn_bias,\n torch::Tensor running_mean,\n torch::Tensor running_var,\n bool training,\n double momentum,\n double eps,\n int64_t cluster_size) {\n const int B = (int)x.size(0);\n const int N = (int)x.size(1);\n const int D = (int)x.size(2);\n const int M = B * N;\n const int C = (int)logits.size(1);\n const int K = (int)cluster_size;\n auto opts = logits.options();\n auto mean = torch::empty({C}, opts);\n auto invstd = torch::empty({C}, opts);\n auto probs = torch::empty({M, K}, opts);\n auto out = torch::empty({B, D * K}, x.options());\n cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n\n if (training) {\n bn_stats_kernel<<<C, 256, 0, stream>>>(logits.data_ptr<float>(), mean.data_ptr<float>(), invstd.data_ptr<float>(),\n running_mean.data_ptr<float>(), running_var.data_ptr<float>(),\n M, C, (float)momentum, (float)eps);\n } else {\n // In eval mode BatchNorm uses the stored running statistics directly.\n mean.copy_(running_mean);\n invstd_eval_kernel<<<(C + 255) / 256, 256, 0, stream>>>(running_var.data_ptr<float>(), invstd.data_ptr<float>(), C, (float)eps);\n }\n bn_softmax_kernel<<<(M + 255) / 256, 256, 0, stream>>>(logits.data_ptr<float>(), mean.data_ptr<float>(), invstd.data_ptr<float>(),\n bn_weight.data_ptr<float>(), bn_bias.data_ptr<float>(),\n probs.data_ptr<float>(), M, C, K);\n dim3 grid(B, K);\n vlad_norm_kernel<<<grid, 256, 0, stream>>>(probs.data_ptr<float>(), x.data_ptr<float>(),\n clusters2.data_ptr<float>(), out.data_ptr<float>(),\n B, N, D, K);\n return out;\n}\n'''\n\n_ext = load_inline(\n name='netvlad_custom_ext_v1',\n cpp_sources=_cpp_src,\n cuda_sources=_cuda_src,\n functions=['netvlad_forward'],\n extra_cuda_cflags=['-O3', '--use_fast_math'],\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 init_sc = (1 / math.sqrt(feature_size))\n clusters = cluster_size + ghost_clusters\n self.clusters = nn.Parameter(init_sc * torch.randn(feature_size, clusters))\n self.batch_norm = nn.BatchNorm1d(clusters)\n self.clusters2 = nn.Parameter(init_sc * torch.randn(1, feature_size, cluster_size))\n self.out_dim = self.cluster_size * feature_size\n\n def forward(self, x, mask=None):\n # Reference ignores mask.\n x_c = x.contiguous()\n x_flat = x_c.view(-1, self.feature_size)\n logits = torch.matmul(x_flat, self.clusters)\n return _ext.netvlad_forward(\n logits, x_c, self.clusters2, self.batch_norm.weight, self.batch_norm.bias,\n self.batch_norm.running_mean, self.batch_norm.running_var,\n bool(self.batch_norm.training), float(self.batch_norm.momentum), float(self.batch_norm.eps),\n int(self.cluster_size)\n )\n"
}