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\ntorch::Tensor gru_forward_cuda(torch::Tensor x, torch::Tensor h0,\n std::vector<torch::Tensor> weights,\n int64_t num_layers, int64_t input_size,\n int64_t hidden_size, bool has_bias,\n bool batch_first);\n'''\n\n_cuda_src = r'''\n#include <torch/extension.h>\n#include <ATen/cuda/CUDAContext.h>\n#include <c10/cuda/CUDAGuard.h>\n#include <cuda_runtime.h>\n#include <cooperative_groups.h>\n#include <vector>\n\nnamespace cg = cooperative_groups;\n\n__device__ __forceinline__ float sigmoid_f(float x) {\n return 1.0f / (1.0f + expf(-x));\n}\n\n__device__ __forceinline__ float load_inp(const float* __restrict__ inp,\n int t, int b, int k,\n int S, int B, int K,\n bool input_is_original,\n bool batch_first) {\n if (input_is_original && batch_first) {\n return inp[(b * S + t) * K + k];\n } else {\n return inp[(t * B + b) * K + k];\n }\n}\n\nextern \"C\" __global__ void gru_dir_kernel(\n const float* __restrict__ inp,\n const float* __restrict__ h0,\n const float* __restrict__ w_ih,\n const float* __restrict__ w_hh,\n const float* __restrict__ b_ih,\n const float* __restrict__ b_hh,\n float* __restrict__ out,\n float* __restrict__ hidden_tmp,\n int S, int B, int K, int H,\n int layer, int dir,\n bool input_is_original,\n bool batch_first,\n bool has_bias,\n bool out_batch_first)\n{\n cg::grid_group grid = cg::this_grid();\n const int tid = blockIdx.x * blockDim.x + threadIdx.x;\n const int stride = blockDim.x * gridDim.x;\n const int total = B * H;\n\n // ping buffer 0 receives the initial hidden state for this layer/direction\n for (int idx = tid; idx < total; idx += stride) {\n int b = idx / H;\n int h = idx - b * H;\n hidden_tmp[idx] = h0[((layer * 2 + dir) * B + b) * H + h];\n }\n grid.sync();\n\n for (int it = 0; it < S; ++it) {\n int t = (dir == 0) ? it : (S - 1 - it);\n const int old_base = (it & 1) ? total : 0;\n const int new_base = (it & 1) ? 0 : total;\n\n for (int idx = tid; idx < total; idx += stride) {\n int b = idx / H;\n int h = idx - b * H;\n\n float ir = has_bias ? b_ih[h] : 0.0f;\n float iz = has_bias ? b_ih[H + h] : 0.0f;\n float in = has_bias ? b_ih[2 * H + h] : 0.0f;\n\n // input affine, rows are laid out as [reset, update, new]\n const float* wr = w_ih + h * K;\n const float* wz = w_ih + (H + h) * K;\n const float* wn = w_ih + (2 * H + h) * K;\n for (int k = 0; k < K; ++k) {\n float xv = load_inp(inp, t, b, k, S, B, K, input_is_original, batch_first);\n ir += xv * wr[k];\n iz += xv * wz[k];\n in += xv * wn[k];\n }\n\n float hr = has_bias ? b_hh[h] : 0.0f;\n float hz = has_bias ? b_hh[H + h] : 0.0f;\n float hn = has_bias ? b_hh[2 * H + h] : 0.0f;\n const float* rr = w_hh + h * H;\n const float* rz = w_hh + (H + h) * H;\n const float* rn = w_hh + (2 * H + h) * H;\n const float* hprev_vec = hidden_tmp + old_base + b * H;\n for (int k = 0; k < H; ++k) {\n float hv = hprev_vec[k];\n hr += hv * rr[k];\n hz += hv * rz[k];\n hn += hv * rn[k];\n }\n\n float r = sigmoid_f(ir + hr);\n float z = sigmoid_f(iz + hz);\n float n = tanhf(in + r * hn);\n float hprev = hprev_vec[h];\n float hnew = (1.0f - z) * n + z * hprev;\n hidden_tmp[new_base + b * H + h] = hnew;\n\n if (out_batch_first) {\n out[(b * S + t) * (2 * H) + dir * H + h] = hnew;\n } else {\n out[(t * B + b) * (2 * H) + dir * H + h] = hnew;\n }\n }\n grid.sync();\n }\n}\n\ntorch::Tensor gru_forward_cuda(torch::Tensor x, torch::Tensor h0,\n std::vector<torch::Tensor> weights,\n int64_t num_layers64, int64_t input_size64,\n int64_t hidden_size64, bool has_bias,\n bool batch_first) {\n TORCH_CHECK(x.is_cuda(), \"x must be CUDA\");\n TORCH_CHECK(h0.is_cuda(), \"h0 must be CUDA\");\n TORCH_CHECK(x.scalar_type() == at::kFloat, \"only float32 supported\");\n TORCH_CHECK(h0.scalar_type() == at::kFloat, \"only float32 supported\");\n TORCH_CHECK(x.is_contiguous(), \"x must be contiguous\");\n TORCH_CHECK(h0.is_contiguous(), \"h0 must be contiguous\");\n for (auto &w : weights) TORCH_CHECK(w.is_cuda() && w.is_contiguous() && w.scalar_type() == at::kFloat, \"weights must be contiguous CUDA float tensors\");\n\n const c10::cuda::CUDAGuard device_guard(x.device());\n int S = (int)(batch_first ? x.size(1) : x.size(0));\n int B = (int)(batch_first ? x.size(0) : x.size(1));\n int input_size = (int)input_size64;\n int H = (int)hidden_size64;\n int L = (int)num_layers64;\n\n auto opts = x.options();\n torch::Tensor buf_a = torch::empty({S, B, 2 * H}, opts);\n torch::Tensor buf_b = (L > 2) ? torch::empty({S, B, 2 * H}, opts) : torch::Tensor();\n torch::Tensor hidden_tmp = torch::empty({2, B, H}, opts);\n torch::Tensor final_out;\n\n const int threads = 128;\n int blocks_n = (B * H + threads - 1) / threads;\n dim3 grid(blocks_n);\n dim3 block(threads);\n cudaStream_t stream = at::cuda::getCurrentCUDAStream();\n\n const int per_layer = has_bias ? 8 : 4;\n const int per_dir = has_bias ? 4 : 2;\n\n torch::Tensor current;\n for (int layer = 0; layer < L; ++layer) {\n int K = (layer == 0) ? input_size : (2 * H);\n bool input_orig = (layer == 0);\n const float* inp_ptr = input_orig ? x.data_ptr<float>() : current.data_ptr<float>();\n\n bool is_last = (layer == L - 1);\n torch::Tensor out;\n if (is_last) {\n if (batch_first) out = torch::empty({B, S, 2 * H}, opts);\n else out = torch::empty({S, B, 2 * H}, opts);\n } else {\n out = (layer & 1) ? buf_b : buf_a;\n }\n\n for (int dir = 0; dir < 2; ++dir) {\n int wi = layer * per_layer + dir * per_dir;\n const float* wih = weights[wi + 0].data_ptr<float>();\n const float* whh = weights[wi + 1].data_ptr<float>();\n const float* bih = has_bias ? weights[wi + 2].data_ptr<float>() : nullptr;\n const float* bhh = has_bias ? weights[wi + 3].data_ptr<float>() : nullptr;\n bool out_bf = is_last && batch_first;\n\n void* args[] = {\n (void*)&inp_ptr, (void*)&(h0.data_ptr<float>()), (void*)&wih, (void*)&whh,\n (void*)&bih, (void*)&bhh, (void*)&(out.data_ptr<float>()), (void*)&(hidden_tmp.data_ptr<float>()),\n (void*)&S, (void*)&B, (void*)&K, (void*)&H,\n (void*)&layer, (void*)&dir, (void*)&input_orig, (void*)&batch_first,\n (void*)&has_bias, (void*)&out_bf\n };\n C10_CUDA_CHECK(cudaLaunchCooperativeKernel((void*)gru_dir_kernel, grid, block, args, 0, stream));\n }\n current = out;\n if (is_last) final_out = out;\n }\n return final_out;\n}\n'''\n\n_ext = load_inline(\n name='custom_gru_bidir_fwd_ext',\n cpp_sources=_cpp_src,\n cuda_sources=_cuda_src,\n functions=['gru_forward_cuda'],\n extra_cflags=['-O3'],\n extra_cuda_cflags=['-O3'],\n with_cuda=True,\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=bias,\n batch_first=batch_first, dropout=0, bidirectional=True)\n\n def _weight_list(self):\n ws = []\n for l in range(self.num_layers):\n for suf in ('', '_reverse'):\n ws.append(getattr(self.gru, 'weight_ih_l%d%s' % (l, suf)))\n ws.append(getattr(self.gru, 'weight_hh_l%d%s' % (l, suf)))\n if self.bias:\n ws.append(getattr(self.gru, 'bias_ih_l%d%s' % (l, suf)))\n ws.append(getattr(self.gru, 'bias_hh_l%d%s' % (l, suf)))\n return ws\n\n def forward(self, x, h0):\n return _ext.gru_forward_cuda(x, h0, self._weight_list(), self.num_layers,\n self.input_size, self.hidden_size,\n self.bias, self.batch_first)\n"
}