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 <ATen/cuda/CUDAContext.h>\n#include <vector>\n\nvoid launch_gru_layer(const at::Tensor& inp,\n at::Tensor& out,\n const at::Tensor& hinit,\n at::Tensor& hfinal,\n const at::Tensor& wihT,\n const at::Tensor& whhT,\n const at::Tensor& bih,\n const at::Tensor& bhh,\n at::Tensor& hbuf,\n at::Tensor& syncbuf,\n int T, int B, int I, int H,\n bool input_batch_first,\n cudaStream_t stream);\n\ntorch::Tensor gru_forward(torch::Tensor x,\n torch::Tensor h0,\n std::vector<torch::Tensor> wihT,\n std::vector<torch::Tensor> whhT,\n std::vector<torch::Tensor> bih,\n std::vector<torch::Tensor> bhh,\n bool batch_first) {\n TORCH_CHECK(x.is_cuda() && h0.is_cuda(), \"inputs must be CUDA tensors\");\n TORCH_CHECK(x.scalar_type() == at::kFloat, \"only fp32 is supported\");\n const int64_t L64 = h0.size(0);\n const int L = (int)L64;\n const int B = (int)h0.size(1);\n const int H = (int)h0.size(2);\n const int T = batch_first ? (int)x.size(1) : (int)x.size(0);\n\n auto hn = at::empty_like(h0);\n at::Tensor cur = x;\n std::vector<at::Tensor> keep;\n keep.reserve(L);\n cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n\n for (int l = 0; l < L; ++l) {\n int I = (int)wihT[l].size(0);\n auto out = at::empty({T, B, H}, x.options());\n auto hbuf = at::empty({2, B, H}, x.options());\n auto syncbuf = at::zeros({2}, x.options().dtype(at::kInt));\n at::Tensor hinit_l = h0.select(0, l);\n at::Tensor hfinal_l = hn.select(0, l);\n launch_gru_layer(cur, out, hinit_l, hfinal_l,\n wihT[l], whhT[l], bih[l], bhh[l],\n hbuf, syncbuf, T, B, I, H,\n (l == 0) && batch_first, stream);\n keep.push_back(out);\n cur = keep.back();\n }\n return hn;\n}\n'''\n\n_cuda_src = r'''\n#include <torch/extension.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n\n#define CHUNK_H 32\n\n__device__ __forceinline__ float sigmoid_f(float x) {\n return 1.0f / (1.0f + expf(-x));\n}\n\n__device__ __forceinline__ void grid_barrier(int total_blocks, int* counter, int* sense, int target) {\n __syncthreads();\n if (threadIdx.x == 0) {\n __threadfence();\n int old = atomicAdd(counter, 1);\n if (old == total_blocks - 1) {\n *counter = 0;\n __threadfence();\n atomicExch(sense, target);\n } else {\n while (atomicAdd(sense, 0) != target) { }\n }\n }\n __syncthreads();\n}\n\n__global__ void gru_layer_kernel(const float* __restrict__ inp,\n float* __restrict__ out,\n const float* __restrict__ hinit,\n float* __restrict__ hfinal,\n const float* __restrict__ wihT,\n const float* __restrict__ whhT,\n const float* __restrict__ bih,\n const float* __restrict__ bhh,\n float* __restrict__ hbuf,\n int* __restrict__ syncbuf,\n int T, int B, int I, int H,\n int chunks, int input_batch_first) {\n __shared__ float sr[CHUNK_H];\n __shared__ float sz[CHUNK_H];\n __shared__ float sni[CHUNK_H];\n __shared__ float snh[CHUNK_H];\n\n const int b = blockIdx.x / chunks;\n const int c = blockIdx.x - b * chunks;\n const int hstart = c * CHUNK_H;\n const int tid = threadIdx.x;\n const int total_blocks = gridDim.x;\n int sense_val = 0;\n\n // initialize recurrent state buffer 0 from h0\n if (tid < CHUNK_H) {\n int h = hstart + tid;\n if (h < H) {\n hbuf[b * H + h] = hinit[b * H + h];\n }\n }\n sense_val = 1 - sense_val;\n grid_barrier(total_blocks, syncbuf, syncbuf + 1, sense_val);\n\n int cur = 0;\n for (int t = 0; t < T; ++t) {\n if (tid < 3 * CHUNK_H) {\n const int gate = tid / CHUNK_H;\n const int off = tid - gate * CHUNK_H;\n const int h = hstart + off;\n if (h < H) {\n float si = 0.0f;\n float sh = 0.0f;\n const int row = gate * H + h;\n\n // input contribution, weights are cached as transposed: [I, 3H]\n for (int k = 0; k < I; ++k) {\n float xv;\n if (input_batch_first) {\n xv = inp[(b * T + t) * I + k];\n } else {\n xv = inp[(t * B + b) * I + k];\n }\n si = fmaf(xv, wihT[k * (3 * H) + row], si);\n }\n // recurrent contribution from previous hidden buffer\n const float* hp = hbuf + cur * (B * H) + b * H;\n for (int k = 0; k < H; ++k) {\n sh = fmaf(hp[k], whhT[k * (3 * H) + row], sh);\n }\n\n if (gate == 0) {\n sr[off] = sigmoid_f(si + sh + bih[row] + bhh[row]);\n } else if (gate == 1) {\n sz[off] = sigmoid_f(si + sh + bih[row] + bhh[row]);\n } else {\n sni[off] = si + bih[row];\n snh[off] = sh + bhh[row];\n }\n }\n }\n __syncthreads();\n\n if (tid < CHUNK_H) {\n int h = hstart + tid;\n if (h < H) {\n float oldh = hbuf[cur * (B * H) + b * H + h];\n float n = tanhf(sni[tid] + sr[tid] * snh[tid]);\n float z = sz[tid];\n float newh = fmaf(z, oldh, (1.0f - z) * n);\n hbuf[(1 - cur) * (B * H) + b * H + h] = newh;\n out[(t * B + b) * H + h] = newh;\n }\n }\n\n sense_val = 1 - sense_val;\n grid_barrier(total_blocks, syncbuf, syncbuf + 1, sense_val);\n cur = 1 - cur;\n }\n\n if (tid < CHUNK_H) {\n int h = hstart + tid;\n if (h < H) {\n hfinal[b * H + h] = hbuf[cur * (B * H) + b * H + h];\n }\n }\n}\n\nvoid launch_gru_layer(const at::Tensor& inp,\n at::Tensor& out,\n const at::Tensor& hinit,\n at::Tensor& hfinal,\n const at::Tensor& wihT,\n const at::Tensor& whhT,\n const at::Tensor& bih,\n const at::Tensor& bhh,\n at::Tensor& hbuf,\n at::Tensor& syncbuf,\n int T, int B, int I, int H,\n bool input_batch_first,\n cudaStream_t stream) {\n int chunks = (H + CHUNK_H - 1) / CHUNK_H;\n dim3 grid(B * chunks);\n dim3 block(128);\n gru_layer_kernel<<<grid, block, 0, stream>>>(\n inp.data_ptr<float>(), out.data_ptr<float>(),\n hinit.data_ptr<float>(), hfinal.data_ptr<float>(),\n wihT.data_ptr<float>(), whhT.data_ptr<float>(),\n bih.data_ptr<float>(), bhh.data_ptr<float>(),\n hbuf.data_ptr<float>(), syncbuf.data_ptr<int>(),\n T, B, I, H, chunks, input_batch_first ? 1 : 0);\n}\n'''\n\n_ext = load_inline(\n name='gru_return_hn_fused_ext',\n cpp_sources=_cpp_src,\n cuda_sources=_cuda_src,\n functions=['gru_forward'],\n extra_cflags=['-O3'],\n extra_cuda_cflags=['-O3'],\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.gru = nn.GRU(input_size, hidden_size, num_layers, bias, batch_first, dropout=0, bidirectional=False)\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._cache_versions = None\n self._cache_device = None\n self._wihT = None\n self._whhT = None\n self._bih = None\n self._bhh = None\n\n def _refresh_cache(self, device):\n params = []\n for l in range(self.num_layers):\n params.append(getattr(self.gru, f'weight_ih_l{l}'))\n params.append(getattr(self.gru, f'weight_hh_l{l}'))\n if self.bias:\n params.append(getattr(self.gru, f'bias_ih_l{l}'))\n params.append(getattr(self.gru, f'bias_hh_l{l}'))\n versions = tuple(p._version for p in params)\n if (self._cache_versions == versions and self._cache_device == device and\n self._wihT is not None):\n return\n self._wihT = []\n self._whhT = []\n self._bih = []\n self._bhh = []\n for l in range(self.num_layers):\n wih = getattr(self.gru, f'weight_ih_l{l}').detach()\n whh = getattr(self.gru, f'weight_hh_l{l}').detach()\n self._wihT.append(wih.t().contiguous())\n self._whhT.append(whh.t().contiguous())\n if self.bias:\n self._bih.append(getattr(self.gru, f'bias_ih_l{l}').detach().contiguous())\n self._bhh.append(getattr(self.gru, f'bias_hh_l{l}').detach().contiguous())\n else:\n self._bih.append(torch.zeros(3 * self.hidden_size, device=device, dtype=torch.float32))\n self._bhh.append(torch.zeros(3 * self.hidden_size, device=device, dtype=torch.float32))\n self._cache_versions = versions\n self._cache_device = device\n\n def forward(self, x, h0):\n self._refresh_cache(x.device)\n return _ext.gru_forward(x.contiguous(), h0.contiguous(), self._wihT, self._whhT, self._bih, self._bhh, self.batch_first)\n"
}