call → submit_kernel
callsubmit_kernel
{
"kernel_code": "import 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#include <vector>\n\nvoid launch_gru_layer(torch::Tensor input, torch::Tensor out, torch::Tensor hn, torch::Tensor h0,\n torch::Tensor wih_f, torch::Tensor whh_f, torch::Tensor bih_f, torch::Tensor bhh_f,\n torch::Tensor wih_b, torch::Tensor whh_b, torch::Tensor bih_b, torch::Tensor bhh_b,\n int S, int B, int input_dim, int H, int layer, bool layer0, bool batch_first,\n bool has_bias, bool store_output);\n\ntorch::Tensor gru_forward(torch::Tensor x, torch::Tensor h0, std::vector<torch::Tensor> params,\n bool batch_first, int64_t num_layers, int64_t input_size, int64_t hidden_size,\n bool has_bias) {\n TORCH_CHECK(x.is_cuda(), \"x must be CUDA\");\n TORCH_CHECK(h0.is_cuda(), \"h0 must be CUDA\");\n int64_t H = hidden_size;\n int64_t S = batch_first ? x.size(1) : x.size(0);\n int64_t B = batch_first ? x.size(0) : x.size(1);\n auto hn = torch::empty_like(h0);\n auto opts = x.options();\n auto buf1 = torch::empty({S, B, 2*H}, opts);\n auto buf2 = torch::empty({S, B, 2*H}, opts);\n torch::Tensor dummy = torch::empty({0}, opts);\n\n for (int l = 0; l < (int)num_layers; ++l) {\n int base = l * 8;\n torch::Tensor inp = (l == 0) ? x : ((l & 1) ? buf1 : buf2);\n torch::Tensor out = (l & 1) ? buf2 : buf1;\n int inpdim = (l == 0) ? (int)input_size : (int)(2*H);\n bool store = (l != (int)num_layers - 1);\n launch_gru_layer(inp, out, hn, h0,\n params[base+0], params[base+1], params[base+2], params[base+3],\n params[base+4], params[base+5], params[base+6], params[base+7],\n (int)S, (int)B, inpdim, (int)H, l, l==0, batch_first, has_bias, store);\n }\n return hn;\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\nstatic __device__ __forceinline__ float sigmoidf_fast(float x) {\n return 1.0f / (1.0f + __expf(-x));\n}\n\n__global__ void gru_layer_kernel(const float* __restrict__ input,\n float* __restrict__ out,\n float* __restrict__ hn,\n const float* __restrict__ h0,\n const float* __restrict__ wih_f,\n const float* __restrict__ whh_f,\n const float* __restrict__ bih_f,\n const float* __restrict__ bhh_f,\n const float* __restrict__ wih_b,\n const float* __restrict__ whh_b,\n const float* __restrict__ bih_b,\n const float* __restrict__ bhh_b,\n int S, int B, int I, int H, int layer,\n int layer0, int batch_first, int has_bias, int store_output) {\n int bd = blockIdx.x;\n int b = bd >> 1;\n int dir = bd & 1;\n int j = threadIdx.x;\n if (j >= H || b >= B) return;\n\n extern __shared__ float smem[];\n float* sh = smem; // H entries: previous hidden\n float* sx = smem + H; // I entries: current input vector\n\n const float* wih = dir == 0 ? wih_f : wih_b;\n const float* whh = dir == 0 ? whh_f : whh_b;\n const float* bih = dir == 0 ? bih_f : bih_b;\n const float* bhh = dir == 0 ? bhh_f : bhh_b;\n\n float hp = h0[((layer * 2 + dir) * B + b) * H + j];\n\n #pragma unroll 1\n for (int step = 0; step < S; ++step) {\n int t = (dir == 0) ? step : (S - 1 - step);\n\n for (int k = j; k < I; k += blockDim.x) {\n if (layer0) {\n sx[k] = batch_first ? input[(b * S + t) * I + k]\n : input[(t * B + b) * I + k];\n } else {\n sx[k] = input[(t * B + b) * I + k];\n }\n }\n sh[j] = hp;\n __syncthreads();\n\n const float* wi_r = wih + 0 * H * I + j * I;\n const float* wi_z = wih + 1 * H * I + j * I;\n const float* wi_n = wih + 2 * H * I + j * I;\n const float* wh_r = whh + 0 * H * H + j * H;\n const float* wh_z = whh + 1 * H * H + j * H;\n const float* wh_n = whh + 2 * H * H + j * H;\n\n float ir = has_bias ? bih[0 * H + j] : 0.0f;\n float iz = has_bias ? bih[1 * H + j] : 0.0f;\n float in = has_bias ? bih[2 * H + j] : 0.0f;\n float hr = has_bias ? bhh[0 * H + j] : 0.0f;\n float hz = has_bias ? bhh[1 * H + j] : 0.0f;\n float hn_lin = has_bias ? bhh[2 * H + j] : 0.0f;\n\n #pragma unroll 1\n for (int k = 0; k < I; ++k) {\n float xv = sx[k];\n ir = fmaf(wi_r[k], xv, ir);\n iz = fmaf(wi_z[k], xv, iz);\n in = fmaf(wi_n[k], xv, in);\n }\n #pragma unroll 1\n for (int k = 0; k < H; ++k) {\n float hv = sh[k];\n hr = fmaf(wh_r[k], hv, hr);\n hz = fmaf(wh_z[k], hv, hz);\n hn_lin = fmaf(wh_n[k], hv, hn_lin);\n }\n\n float r = sigmoidf_fast(ir + hr);\n float z = sigmoidf_fast(iz + hz);\n float n = tanhf(in + r * hn_lin);\n hp = fmaf(z, hp - n, n); // (1-z)*n + z*hp\n\n if (store_output) {\n out[(t * B + b) * (2 * H) + dir * H + j] = hp;\n }\n __syncthreads();\n }\n hn[((layer * 2 + dir) * B + b) * H + j] = hp;\n}\n\nvoid launch_gru_layer(torch::Tensor input, torch::Tensor out, torch::Tensor hn, torch::Tensor h0,\n torch::Tensor wih_f, torch::Tensor whh_f, torch::Tensor bih_f, torch::Tensor bhh_f,\n torch::Tensor wih_b, torch::Tensor whh_b, torch::Tensor bih_b, torch::Tensor bhh_b,\n int S, int B, int input_dim, int H, int layer, bool layer0, bool batch_first,\n bool has_bias, bool store_output) {\n dim3 grid(B * 2);\n dim3 block(H);\n size_t shmem = (size_t)(H + input_dim) * sizeof(float);\n cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n gru_layer_kernel<<<grid, block, shmem, stream>>>(\n input.data_ptr<float>(), out.data_ptr<float>(), hn.data_ptr<float>(), h0.data_ptr<float>(),\n wih_f.data_ptr<float>(), whh_f.data_ptr<float>(), bih_f.data_ptr<float>(), bhh_f.data_ptr<float>(),\n wih_b.data_ptr<float>(), whh_b.data_ptr<float>(), bih_b.data_ptr<float>(), bhh_b.data_ptr<float>(),\n S, B, input_dim, H, layer, layer0 ? 1 : 0, batch_first ? 1 : 0, has_bias ? 1 : 0, store_output ? 1 : 0);\n}\n'''\n\n_ext = load_inline(\n name=\"custom_gru_hn_ext_v1\",\n cpp_sources=[_cpp_src],\n cuda_sources=[_cuda_src],\n functions=[\"gru_forward\"],\n extra_cflags=[\"-O3\"],\n extra_cuda_cflags=[\"-O3\", \"--use_fast_math\"],\n verbose=False,\n)\n\nclass ModelNew(nn.Module):\n def __init__(self, input_size, hidden_size, num_layers=3, bias=True, batch_first=False):\n super(ModelNew, self).__init__()\n self.input_size = input_size\n self.hidden_size = hidden_size\n self.num_layers = num_layers\n self.bias = bias\n self.batch_first = batch_first\n self.gru = nn.GRU(input_size, hidden_size, num_layers, bias, batch_first, dropout=0, bidirectional=True)\n\n def _param_list(self, device):\n ps = []\n dummy = None\n if not self.bias:\n dummy = torch.empty((0,), device=device, dtype=self.gru.weight_ih_l0.dtype)\n for l in range(self.num_layers):\n for suffix in (\"\", \"_reverse\"):\n ps.append(getattr(self.gru, f\"weight_ih_l{l}{suffix}\"))\n ps.append(getattr(self.gru, f\"weight_hh_l{l}{suffix}\"))\n if self.bias:\n ps.append(getattr(self.gru, f\"bias_ih_l{l}{suffix}\"))\n ps.append(getattr(self.gru, f\"bias_hh_l{l}{suffix}\"))\n else:\n ps.append(dummy); ps.append(dummy)\n return ps\n\n def forward(self, x, h0):\n return _ext.gru_forward(x, h0, self._param_list(x.device), self.batch_first,\n self.num_layers, self.input_size, self.hidden_size, self.bias)\n"
}