call → compile_kernel
callcompile_kernel
{
"kernel_code": "import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.utils.cpp_extension import load_inline\n\ncuda_source = r\"\"\"\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n#include <cmath>\n\ntemplate <typename scalar_t>\n__device__ __forceinline__ scalar_t silu(scalar_t x) {\n return x / (scalar_t(1.0) + exp(-x));\n}\n\n// Kernel for fused MLP + max pooling\n// Each thread processes one (b, i) pair and iterates over j\n// Uses shared memory for intermediate buffers\ntemplate <typename scalar_t>\n__global__ void fused_mlp_maxpool_kernel(\n const scalar_t* __restrict__ nodes,\n const scalar_t* __restrict__ pair_feats,\n const scalar_t* __restrict__ edge_mask,\n const scalar_t* __restrict__ w1,\n const scalar_t* __restrict__ b1,\n const scalar_t* __restrict__ w2,\n const scalar_t* __restrict__ b2,\n scalar_t* __restrict__ pooled,\n int B, int N, int D, int P, int H) {\n \n int b = blockIdx.y;\n int i = blockIdx.x * blockDim.x + threadIdx.x;\n \n if (b >= B || i >= N) return;\n \n // Use dynamic shared memory\n extern __shared__ char shared_buf[];\n scalar_t* node_i_shared = reinterpret_cast<scalar_t*>(shared_buf);\n scalar_t* hidden_shared = node_i_shared + D; // Hidden buffer\n \n // Load node i into shared memory (we'll reuse this for all j)\n for (int d = threadIdx.x; d < D; d += blockDim.x) {\n node_i_shared[d] = nodes[(b * N + i) * D + d];\n }\n __syncthreads();\n \n // Each thread needs its own node_i copy in registers - but for small D, use registers\n // For D=96, P=16, H=192, we need to be careful\n \n // Negative infinity for masking\n scalar_t neg_inf = -1e30f;\n \n // Initialize pooled result in registers (each thread handles all D for one (b,i))\n // Use local arrays - will be promoted to registers by compiler\n // But we need to ensure size is known at compile time...\n // For now, use max sizes: D=128 max\n scalar_t pooled_out[128];\n for (int d = 0; d < D; d++) {\n pooled_out[d] = neg_inf;\n }\n \n // Iterate over j\n for (int j = 0; j < N; j++) {\n // Check edge mask\n scalar_t mask = edge_mask[b * N * N + i * N + j];\n if (mask < scalar_t(0.5)) continue;\n \n // Load node j\n scalar_t node_j[128];\n for (int d = 0; d < D; d++) {\n node_j[d] = nodes[(b * N + j) * D + d];\n }\n \n // Load pair features for (i,j)\n scalar_t pf[64]; // P max = 64\n for (int p = 0; p < P; p++) {\n pf[p] = pair_feats[((b * N + i) * N + j) * P + p];\n }\n \n // First linear: input = [node_i, node_j, pf], size = 2D + P\n // Output = hidden, size = H\n scalar_t hidden1[256]; // H max = 256\n \n for (int h = 0; h < H; h++) {\n scalar_t sum = b1[h];\n // node_i contribution\n for (int d = 0; d < D; d++) {\n sum += node_i_shared[d] * w1[d * H + h];\n }\n // node_j contribution\n for (int d = 0; d < D; d++) {\n sum += node_j[d] * w1[(D + d) * H + h];\n }\n // pair_feat contribution\n for (int p = 0; p < P; p++) {\n sum += pf[p] * w1[(2 * D + p) * H + h];\n }\n hidden1[h] = sum;\n }\n \n // SiLU activation\n for (int h = 0; h < H; h++) {\n hidden1[h] = silu(hidden1[h]);\n }\n \n // Second linear: input = hidden, size = H, output = D\n scalar_t output[128];\n for (int d = 0; d < D; d++) {\n scalar_t sum = b2[d];\n for (int h = 0; h < H; h++) {\n sum += hidden1[h] * w2[h * D + d];\n }\n output[d] = sum;\n }\n \n // Max pooling\n for (int d = 0; d < D; d++) {\n if (output[d] > pooled_out[d]) {\n pooled_out[d] = output[d];\n }\n }\n }\n \n // Store pooled result\n for (int d = 0; d < D; d++) {\n pooled[(b * N + i) * D + d] = pooled_out[d];\n }\n}\n\n// LayerNorm kernel\ntemplate <typename scalar_t>\n__global__ void layernorm_kernel(\n const scalar_t* __restrict__ input,\n const scalar_t* __restrict__ weight,\n const scalar_t* __restrict__ bias,\n scalar_t* __restrict__ output,\n int D, int total_elements) {\n \n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n if (idx >= total_elements / D) return;\n \n int base = idx * D;\n \n // Compute mean\n scalar_t mean = scalar_t(0.0);\n for (int d = 0; d < D; d++) {\n mean += input[base + d];\n }\n mean /= scalar_t(D);\n \n // Compute variance\n scalar_t var = scalar_t(0.0);\n for (int d = 0; d < D; d++) {\n scalar_t diff = input[base + d] - mean;\n var += diff * diff;\n }\n var /= scalar_t(D);\n \n // Normalize with eps=1e-5\n scalar_t inv_std = rsqrt(var + scalar_t(1e-5));\n \n // Apply weight and bias\n for (int d = 0; d < D; d++) {\n output[base + d] = (input[base + d] - mean) * inv_std * weight[d] + bias[d];\n }\n}\n\n// Residual add kernel\ntemplate <typename scalar_t>\n__global__ void residual_add_kernel(\n const scalar_t* __restrict__ nodes,\n const scalar_t* __restrict__ normalized,\n scalar_t* __restrict__ output,\n int total_elements) {\n \n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n if (idx >= total_elements) return;\n \n output[idx] = nodes[idx] + normalized[idx];\n}\n\ntorch::Tensor forward_cuda(\n torch::Tensor nodes,\n torch::Tensor pair_feats,\n torch::Tensor edge_mask,\n torch::Tensor w1,\n torch::Tensor b1,\n torch::Tensor w2,\n torch::Tensor b2,\n torch::Tensor ln_weight,\n torch::Tensor ln_bias) {\n \n int B = nodes.size(0);\n int N = nodes.size(1);\n int D = nodes.size(2);\n int P = pair_feats.size(3);\n int H = w1.size(1);\n \n auto options = nodes.options();\n auto pooled = torch::empty({B, N, D}, options);\n auto normalized = torch::empty({B, N, D}, options);\n auto output = torch::empty({B, N, D}, options);\n \n dim3 block(32); // Use 32 threads per block\n dim3 grid((N + block.x - 1) / block.x, B);\n \n // Shared memory size: D * sizeof(scalar_t) for node_i\n size_t shared_mem_size = D * sizeof(float); // Use float size as upper bound\n \n AT_DISPATCH_FLOATING_TYPES(nodes.scalar_type(), \"fused_mlp_maxpool\", ([&] {\n fused_mlp_maxpool_kernel<scalar_t><<<grid, block, shared_mem_size>>>(\n nodes.data_ptr<scalar_t>(),\n pair_feats.data_ptr<scalar_t>(),\n edge_mask.data_ptr<scalar_t>(),\n w1.data_ptr<scalar_t>(),\n b1.data_ptr<scalar_t>(),\n w2.data_ptr<scalar_t>(),\n b2.data_ptr<scalar_t>(),\n pooled.data_ptr<scalar_t>(),\n B, N, D, P, H);\n }));\n \n // LayerNorm\n int total = B * N;\n int threads = 256;\n int blocks = (total + threads - 1) / threads;\n \n AT_DISPATCH_FLOATING_TYPES(nodes.scalar_type(), \"layernorm\", ([&] {\n layernorm_kernel<scalar_t><<<blocks, threads>>>(\n pooled.data_ptr<scalar_t>(),\n ln_weight.data_ptr<scalar_t>(),\n ln_bias.data_ptr<scalar_t>(),\n normalized.data_ptr<scalar_t>(),\n D, total * D);\n }));\n \n // Residual add\n int total_elements = B * N * D;\n blocks = (total_elements + threads - 1) / threads;\n \n AT_DISPATCH_FLOATING_TYPES(nodes.scalar_type(), \"residual_add\", ([&] {\n residual_add_kernel<scalar_t><<<blocks, threads>>>(\n nodes.data_ptr<scalar_t>(),\n normalized.data_ptr<scalar_t>(),\n output.data_ptr<scalar_t>(),\n total_elements);\n }));\n \n return output;\n}\n\"\"\"\n\ncpp_source = \"torch::Tensor forward_cuda(torch::Tensor nodes, torch::Tensor pair_feats, torch::Tensor edge_mask, torch::Tensor w1, torch::Tensor b1, torch::Tensor w2, torch::Tensor b2, torch::Tensor ln_weight, torch::Tensor ln_bias);\"\n\nfused_module = load_inline(\n name='fused_scene_graph',\n cpp_sources=cpp_source,\n cuda_sources=cuda_source,\n functions=['forward_cuda'],\n extra_cuda_cflags=['-O3'],\n verbose=True\n)\n\nimport torch\nimport torch.nn as nn\n\nclass Model(nn.Module):\n def __init__(self, dim: int, pair_dim: int, hidden: int):\n super().__init__()\n self.dim = dim\n self.pair_dim = pair_dim\n self.fuse = nn.Sequential(\n nn.Linear(2 * dim + pair_dim, hidden),\n nn.SiLU(),\n nn.Linear(hidden, dim),\n )\n self.out_norm = nn.LayerNorm(dim)\n\n def forward(self, nodes, pair_feats, edge_mask):\n B, N, D = nodes.shape\n hi = nodes.unsqueeze(2).expand(-1, -1, N, -1)\n hj = nodes.unsqueeze(1).expand(-1, N, -1, -1)\n fused = self.fuse(torch.cat([hi, hj, pair_feats], dim=-1))\n neg = torch.finfo(fused.dtype).min\n fused = fused.masked_fill(edge_mask.unsqueeze(-1) < 0.5, neg)\n pooled, _ = fused.max(dim=2)\n return nodes + self.out_norm(pooled)\n\n\nclass ModelNew(nn.Module):\n def __init__(self, dim: int, pair_dim: int, hidden: int):\n super().__init__()\n self.dim = dim\n self.pair_dim = pair_dim\n self.hidden = hidden\n \n # Create PyTorch layers to hold weights\n self.fuse = nn.Sequential(\n nn.Linear(2 * dim + pair_dim, hidden),\n nn.SiLU(),\n nn.Linear(hidden, dim),\n )\n self.out_norm = nn.LayerNorm(dim)\n \n def forward(self, nodes: torch.Tensor, pair_feats: torch.Tensor, edge_mask: torch.Tensor) -> torch.Tensor:\n # Get weights from PyTorch layers\n w1 = self.fuse[0].weight.t().contiguous() # (in_features, out_features) -> transpose\n b1 = self.fuse[0].bias.contiguous()\n w2 = self.fuse[2].weight.t().contiguous()\n b2 = self.fuse[2].bias.contiguous()\n ln_weight = self.out_norm.weight.contiguous()\n ln_bias = self.out_norm.bias.contiguous()\n \n return fused_module.forward_cuda(\n nodes, pair_feats, edge_mask,\n w1, b1, w2, b2, ln_weight, ln_bias\n )\n\n\nbatch_size = 4\nnum_objects = 24\ndim = 96\npair_dim = 16\nhidden = 192\n\n\ndef get_inputs():\n nodes = torch.randn(batch_size, num_objects, dim)\n pair_feats = torch.randn(batch_size, num_objects, num_objects, pair_dim)\n edge_mask = torch.ones(batch_size, num_objects, num_objects)\n return [nodes, pair_feats, edge_mask]\n\n\ndef get_init_inputs():\n return [dim, pair_dim, hidden]"
}