call → run_correctness
callrun_correctness
{
"kernel_code": "import torch\nimport torch.nn as nn\nfrom torch.utils.cpp_extension import load_inline\n\n_cpp_sources = r'''\n#include <torch/extension.h>\ntorch::Tensor scatter_add_forward(torch::Tensor a, torch::Tensor b, int64_t num_nodes);\n'''\n_cuda_sources = r'''\n#include <torch/extension.h>\n#include <ATen/cuda/CUDAContext.h>\n#include <c10/cuda/CUDAGuard.h>\n\n__global__ void zero_kernel(float* __restrict__ out, int n) { int i=blockIdx.x*blockDim.x+threadIdx.x; if(i<n) out[i]=0.0f; }\n\ntemplate <typename IdxT>\n__global__ void node_scan64_kernel(const IdxT* __restrict__ dst, const float* __restrict__ edge, float* __restrict__ out, int E) {\n int node = blockIdx.x;\n __shared__ float acc[64];\n int tid = threadIdx.x;\n if (tid < 64) acc[tid] = 0.0f;\n __syncthreads();\n int lane = tid & 31;\n const unsigned FULL = 0xffffffffu;\n #pragma unroll 1\n for (int e = tid; e < E; e += blockDim.x) {\n bool hit = ((int)dst[e] == node);\n unsigned mask = __ballot_sync(FULL, hit);\n while (mask) {\n int src_lane = __ffs(mask) - 1;\n int me = __shfl_sync(FULL, e, src_lane);\n const float* row = edge + ((int64_t)me << 6);\n atomicAdd(&acc[lane], row[lane]);\n atomicAdd(&acc[lane + 32], row[lane + 32]);\n mask &= mask - 1;\n }\n }\n __syncthreads();\n if (tid < 64) out[((int64_t)node << 6) + tid] = acc[tid];\n}\n\ntemplate <typename IdxT>\n__global__ void scatter64_vec4_kernel(const IdxT* __restrict__ dst, const float* __restrict__ edge, float* __restrict__ out, int E) {\n int idx=blockIdx.x*blockDim.x+threadIdx.x; int total=E<<4;\n if(idx<total){ int e=idx>>4; int g=idx&15; int d=(int)dst[e]; const float4 v=reinterpret_cast<const float4*>(edge+(e<<6))[g]; float* o=out+((int64_t)d<<6)+(g<<2); atomicAdd(o,v.x); atomicAdd(o+1,v.y); atomicAdd(o+2,v.z); atomicAdd(o+3,v.w); }\n}\ntemplate <typename IdxT>\n__global__ void scatter_generic_kernel(const IdxT* __restrict__ dst, const float* __restrict__ edge, float* __restrict__ out, int E, int D) { int idx=blockIdx.x*blockDim.x+threadIdx.x; int total=E*D; if(idx<total){ int e=idx/D; int f=idx-e*D; atomicAdd(out+(int64_t)((int)dst[e])*D+f, edge[idx]); } }\n\ntorch::Tensor scatter_add_forward(torch::Tensor a, torch::Tensor b, int64_t num_nodes) {\n torch::Tensor dst_idx=a, edge_feat=b;\n if(edge_feat.dim()<2 && dst_idx.dim()>=2){ dst_idx=b; edge_feat=a; }\n const at::cuda::OptionalCUDAGuard device_guard(device_of(edge_feat));\n int64_t E64=edge_feat.size(0), D64=edge_feat.size(1);\n auto out=torch::empty({num_nodes,D64}, edge_feat.options()); float* outp=out.data_ptr<float>();\n cudaStream_t stream=at::cuda::getCurrentCUDAStream(); const int threads=256; int E=(int)E64;\n if(D64==64 && num_nodes <= 4096) {\n dim3 grid((unsigned)num_nodes);\n if(dst_idx.scalar_type()==at::kInt) node_scan64_kernel<int><<<grid,threads,0,stream>>>(dst_idx.data_ptr<int>(), edge_feat.data_ptr<float>(), outp, E);\n else if(dst_idx.scalar_type()==at::kLong) node_scan64_kernel<int64_t><<<grid,threads,0,stream>>>(dst_idx.data_ptr<int64_t>(), edge_feat.data_ptr<float>(), outp, E);\n else node_scan64_kernel<float><<<grid,threads,0,stream>>>(dst_idx.data_ptr<float>(), edge_feat.data_ptr<float>(), outp, E);\n } else {\n int nout=(int)(num_nodes*D64); zero_kernel<<<(nout+threads-1)/threads,threads,0,stream>>>(outp,nout);\n int total=(D64==64)?(E<<4):(int)(E64*D64); int blocks=(total+threads-1)/threads;\n if(D64==64){\n if(dst_idx.scalar_type()==at::kInt) scatter64_vec4_kernel<int><<<blocks,threads,0,stream>>>(dst_idx.data_ptr<int>(), edge_feat.data_ptr<float>(), outp, E);\n else if(dst_idx.scalar_type()==at::kLong) scatter64_vec4_kernel<int64_t><<<blocks,threads,0,stream>>>(dst_idx.data_ptr<int64_t>(), edge_feat.data_ptr<float>(), outp, E);\n else scatter64_vec4_kernel<float><<<blocks,threads,0,stream>>>(dst_idx.data_ptr<float>(), edge_feat.data_ptr<float>(), outp, E);\n } else {\n if(dst_idx.scalar_type()==at::kInt) scatter_generic_kernel<int><<<blocks,threads,0,stream>>>(dst_idx.data_ptr<int>(), edge_feat.data_ptr<float>(), outp, E, (int)D64);\n else if(dst_idx.scalar_type()==at::kLong) scatter_generic_kernel<int64_t><<<blocks,threads,0,stream>>>(dst_idx.data_ptr<int64_t>(), edge_feat.data_ptr<float>(), outp, E, (int)D64);\n else scatter_generic_kernel<float><<<blocks,threads,0,stream>>>(dst_idx.data_ptr<float>(), edge_feat.data_ptr<float>(), outp, E, (int)D64);\n }\n }\n return out;\n}\n'''\n_ext=load_inline(name='scatter_add_ext_v5', cpp_sources=_cpp_sources, cuda_sources=_cuda_sources, functions=['scatter_add_forward'], extra_cuda_cflags=['-O3'], extra_cflags=['-O3'], with_cuda=True, verbose=False)\nclass ModelNew(nn.Module):\n def __init__(self, num_nodes): super().__init__(); self.num_nodes=int(num_nodes)\n def forward(self, dst_idx: torch.Tensor, edge_feat: torch.Tensor) -> torch.Tensor:\n if edge_feat.dim()<2 and dst_idx.dim()>=2: dst_idx, edge_feat = edge_feat, dst_idx\n return _ext.scatter_add_forward(dst_idx, edge_feat, self.num_nodes)\n"
}