megamoe_rm.cu 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. // SPDX-License-Identifier: Apache-2.0
  2. // megamoe on CUTLASS's own NVFP4 MoE layout (the tensors vLLM's FlashInfer
  3. // CUTLASS backend keeps after processing), so decode and prefill share one copy
  4. // of the weights: this kernel for decode-sized batches, CUTLASS for prefill.
  5. //
  6. // That layout, per expert: w13 [2I][K/2] bytes, rows 0..I-1 up and I..2I-1
  7. // gate; w2 [H][I/2]; block scales e4m3 in FlashInfer's 128x4 swizzle; weight
  8. // global scale = weight_scale_2 / input_scale (processing folded the input
  9. // scale in). W4A16 as in megamoe.cu: activations stay fp16, exact in weights.
  10. //
  11. // A 16-row weight tile is contiguous in this layout. FC1 streams tiles into
  12. // shared memory with cp.async, 128 bytes per row per copy, so DRAM sees long
  13. // runs; loading mma fragments straight from global memory spans 16 rows per
  14. // instruction and reaches only ~205 GB/s. Inside a 64-byte chunk the bytes are
  15. // already in the order chunk_mma wants (lane t owns k 32t..32t+31).
  16. #include <ATen/cuda/CUDAContext.h>
  17. #include <cuda_fp16.h>
  18. #include <torch/extension.h>
  19. namespace {
  20. constexpr int kSlots = 8; // tokens per expert entry: the mma's N
  21. __device__ __forceinline__ uint4 ld_stream(const void* p) {
  22. uint4 v;
  23. asm volatile("ld.global.nc.L1::no_allocate.v4.u32 {%0,%1,%2,%3}, [%4];"
  24. : "=r"(v.x), "=r"(v.y), "=r"(v.z), "=r"(v.w)
  25. : "l"(p));
  26. return v;
  27. }
  28. __device__ __forceinline__ uint32_t e2m1x2(uint32_t byte) {
  29. uint32_t r;
  30. asm("{ .reg .b8 t; cvt.u8.u32 t, %1; cvt.rn.f16x2.e2m1x2 %0, t; }" : "=r"(r) : "r"(byte));
  31. return r;
  32. }
  33. __device__ __forceinline__ uint32_t e4m3x2(uint32_t two) {
  34. uint32_t r;
  35. asm("{ .reg .b16 t; cvt.u16.u32 t, %1; cvt.rn.f16x2.e4m3x2 %0, t; }" : "=r"(r) : "r"(two));
  36. return r;
  37. }
  38. __device__ __forceinline__ uint32_t hmul2(uint32_t a, uint32_t b) {
  39. __half2 r = __hmul2(*reinterpret_cast<__half2*>(&a), *reinterpret_cast<__half2*>(&b));
  40. return *reinterpret_cast<uint32_t*>(&r);
  41. }
  42. // Broadcast the low or high half of a half2 to both lanes.
  43. __device__ __forceinline__ uint32_t bcast(uint32_t h2, int hi) {
  44. uint32_t v = hi ? (h2 >> 16) : (h2 & 0xffff);
  45. return v | (v << 16);
  46. }
  47. __device__ __forceinline__ void mma16816(float c[4], const uint32_t a[4], uint32_t b0, uint32_t b1) {
  48. asm volatile(
  49. "mma.sync.aligned.m16n8k16.row.col.f32.f16.f16.f32 "
  50. "{%0,%1,%2,%3}, {%4,%5,%6,%7}, {%8,%9}, {%0,%1,%2,%3};"
  51. : "+f"(c[0]), "+f"(c[1]), "+f"(c[2]), "+f"(c[3])
  52. : "r"(a[0]), "r"(a[1]), "r"(a[2]), "r"(a[3]), "r"(b0), "r"(b1));
  53. }
  54. __device__ __forceinline__ uint32_t word(const uint4& v, int i) {
  55. return i == 0 ? v.x : i == 1 ? v.y : i == 2 ? v.z : v.w;
  56. }
  57. // One 128-wide k chunk of a 16x8 tile: rows g and g+8 from w0/w1, their
  58. // packed scales, and this lane's 32 activation values x[0..3].
  59. __device__ __forceinline__ void chunk_mma(float c[4], const uint4& w0, const uint4& w1,
  60. uint32_t sc, const uint4 x[4]) {
  61. const uint32_t s_lo = e4m3x2(sc & 0xffff); // row g: block 2t, 2t+1
  62. const uint32_t s_hi = e4m3x2(sc >> 16); // row g+8: block 2t, 2t+1
  63. #pragma unroll
  64. for (int j = 0; j < 8; ++j) {
  65. const int blk = j >> 2;
  66. const uint32_t sg = bcast(s_lo, blk), sg8 = bcast(s_hi, blk);
  67. const uint32_t p0 = (word(w0, j >> 1) >> ((j & 1) * 16)) & 0xffff;
  68. const uint32_t p1 = (word(w1, j >> 1) >> ((j & 1) * 16)) & 0xffff;
  69. uint32_t a[4];
  70. a[0] = hmul2(e2m1x2(p0 & 0xff), sg); // row g, k+0..1
  71. a[1] = hmul2(e2m1x2(p1 & 0xff), sg8); // row g+8, k+0..1
  72. a[2] = hmul2(e2m1x2(p0 >> 8), sg); // row g, k+2..3
  73. a[3] = hmul2(e2m1x2(p1 >> 8), sg8); // row g+8, k+2..3
  74. const uint4& xv = x[j >> 1];
  75. const int wi = (j & 1) * 2;
  76. mma16816(c, a, word(xv, wi), word(xv, wi + 1));
  77. }
  78. }
  79. // Groups the M*topk (token, k) pairs by expert, eight tokens per entry, in
  80. // ascending expert order, and converts x to fp16. One block.
  81. __global__ void route_prep(const int* __restrict__ topk_ids, int M, int topk, int E,
  82. const __nv_bfloat16* __restrict__ x, int K, __half* __restrict__ xh,
  83. int* __restrict__ n_entries, int* __restrict__ entry_expert,
  84. int* __restrict__ entry_tk) {
  85. extern __shared__ int sm[];
  86. int* cnt = sm; // [E]
  87. int* base = sm + E; // [E]
  88. const int tid = threadIdx.x, nt = blockDim.x;
  89. for (int i = tid; i < M * K; i += nt) xh[i] = __float2half_rn(__bfloat162float(x[i]));
  90. for (int e = tid; e < E; e += nt) cnt[e] = 0;
  91. __syncthreads();
  92. const int pairs = M * topk;
  93. int my_pos[4];
  94. // A pair's slot is its rank among earlier pairs with the same expert, so
  95. // every block that rebuilds this table agrees on it.
  96. // A pair with expert id -1 is skipped: no entry, so no weight reads, and
  97. // combine leaves it out.
  98. for (int i = tid, n = 0; i < pairs; i += nt, ++n) {
  99. const int e = topk_ids[i];
  100. if (e < 0) continue;
  101. int r = 0;
  102. for (int j = 0; j < i; ++j) r += topk_ids[j] == e;
  103. my_pos[n] = r;
  104. atomicAdd(&cnt[e], 1);
  105. }
  106. __syncthreads();
  107. // Exclusive scan of entries-per-expert (ceil(cnt/8)) across the block,
  108. // one expert per thread: E <= blockDim.
  109. int* wsum = sm + 2 * E; // [32]
  110. const int lane = tid & 31, wid = tid >> 5;
  111. const int mine = tid < E ? (cnt[tid] + kSlots - 1) / kSlots : 0;
  112. int incl = mine;
  113. #pragma unroll
  114. for (int o = 1; o < 32; o <<= 1) {
  115. const int v = __shfl_up_sync(0xffffffffu, incl, o);
  116. if (lane >= o) incl += v;
  117. }
  118. if (lane == 31) wsum[wid] = incl;
  119. for (int i = tid; i < pairs * kSlots; i += nt) entry_tk[i] = -1;
  120. for (int i = tid; i < pairs; i += nt) entry_expert[i] = -1;
  121. __syncthreads();
  122. if (wid == 0) {
  123. int s = lane < nt / 32 ? wsum[lane] : 0;
  124. #pragma unroll
  125. for (int o = 1; o < 32; o <<= 1) {
  126. const int v = __shfl_up_sync(0xffffffffu, s, o);
  127. if (lane >= o) s += v;
  128. }
  129. wsum[lane] = s; // inclusive per-warp totals
  130. }
  131. __syncthreads();
  132. const int excl = incl - mine + (wid ? wsum[wid - 1] : 0);
  133. if (tid < E) {
  134. base[tid] = excl;
  135. for (int j = 0; j < mine; ++j) entry_expert[excl + j] = tid;
  136. }
  137. if (tid == nt - 1) *n_entries = wsum[nt / 32 - 1];
  138. __syncthreads();
  139. for (int i = tid, n = 0; i < pairs; i += nt, ++n) {
  140. const int e = topk_ids[i], p = my_pos[n];
  141. if (e < 0) continue;
  142. entry_tk[(base[e] + p / kSlots) * kSlots + p % kSlots] = i; // i = token*topk + k
  143. }
  144. }
  145. __device__ __forceinline__ size_t scale_off(int r, int c, int C) { // 128x4 swizzle within one expert
  146. return ((size_t)(r >> 7) * (C >> 2) + (c >> 2)) * 512 + (r & 31) * 16 + ((r >> 5) & 3) * 4 + (c & 3);
  147. }
  148. // Scales for chunk_mma: {row a: blocks c, c+1; row b: blocks c, c+1}.
  149. __device__ __forceinline__ uint32_t scale_pair(const uint8_t* s, int ra, int rb, int c, int C) {
  150. const uint32_t lo = *reinterpret_cast<const uint16_t*>(s + scale_off(ra, c, C));
  151. const uint32_t hi = *reinterpret_cast<const uint16_t*>(s + scale_off(rb, c, C));
  152. return lo | (hi << 16);
  153. }
  154. __device__ __forceinline__ void cp_async16(void* dst, const void* src) {
  155. const uint32_t d = static_cast<uint32_t>(__cvta_generic_to_shared(dst));
  156. asm volatile("cp.async.cg.shared.global.L2::128B [%0], [%1], 16;" ::"r"(d), "l"(src));
  157. }
  158. __device__ __forceinline__ void cp_commit() { asm volatile("cp.async.commit_group;"); }
  159. template <int N>
  160. __device__ __forceinline__ void cp_wait() { asm volatile("cp.async.wait_group %0;" ::"n"(N)); }
  161. constexpr int kRowBytes = 128; // bytes per row per stage (256 k)
  162. constexpr int kRowStride = 192; // padded: rows g and g+1 land on disjoint banks
  163. constexpr int kStageBytes = 32 * kRowStride; // 16 up rows + 16 gate rows
  164. // FC1 + GLM SwiGLU for one expert entry and 16 output rows. KSPLIT warps
  165. // split K; each double-buffers its slice through shared memory.
  166. template <int KSPLIT>
  167. __global__ void __launch_bounds__(KSPLIT * 32) fc1_rm(
  168. const uint8_t* __restrict__ w13, const uint8_t* __restrict__ s13,
  169. const float* __restrict__ alpha1, const __half* __restrict__ xh, int topk,
  170. const int* __restrict__ n_entries, const int* __restrict__ entry_expert,
  171. const int* __restrict__ entry_tk, __half* __restrict__ hbuf, int K, int I, float limit) {
  172. extern __shared__ __align__(16) uint8_t smem[];
  173. const int u = blockIdx.x;
  174. if (u >= *n_entries) return;
  175. const int e = entry_expert[u], tile = blockIdx.y;
  176. const int ks = threadIdx.x >> 5, lane = threadIdx.x & 31, g = lane >> 2, t = lane & 3;
  177. const int tk = entry_tk[u * kSlots + g];
  178. const __half* xrow = tk >= 0 ? xh + (size_t)(tk / topk) * K + 32 * t : nullptr;
  179. const int C = K / 16, rowb = K / 2;
  180. const uint8_t* wexp = w13 + (size_t)e * 2 * I * rowb;
  181. const uint8_t* sexp = s13 + (size_t)e * 2 * I * C;
  182. uint8_t* buf = smem + ks * 2 * kStageBytes;
  183. const int kb0 = rowb / KSPLIT * ks, nst = rowb / KSPLIT / kRowBytes;
  184. // This lane's copies: rows 4i + lane/8, 16-byte segment lane%8.
  185. auto issue = [&](int st) {
  186. uint8_t* dst = buf + (st & 1) * kStageBytes;
  187. const int kb = kb0 + st * kRowBytes;
  188. #pragma unroll
  189. for (int i = 0; i < 8; ++i) {
  190. const int row = 4 * i + (lane >> 3), seg = lane & 7;
  191. const int wrow = row < 16 ? tile * 16 + row : I + tile * 16 + row - 16;
  192. cp_async16(dst + row * kRowStride + seg * 16, wexp + (size_t)wrow * rowb + kb + seg * 16);
  193. }
  194. cp_commit();
  195. };
  196. float cg[4] = {0.f, 0.f, 0.f, 0.f}, cu[4] = {0.f, 0.f, 0.f, 0.f};
  197. issue(0);
  198. for (int st = 0; st < nst; ++st) {
  199. if (st + 1 < nst) { issue(st + 1); cp_wait<1>(); } else { cp_wait<0>(); }
  200. __syncwarp();
  201. const uint8_t* b = buf + (st & 1) * kStageBytes;
  202. const int kbyte = kb0 + st * kRowBytes;
  203. #pragma unroll
  204. for (int j = 0; j < 2; ++j) { // two 128-k chunks per stage
  205. const int k = kbyte * 2 + j * 128, c = k / 16 + 2 * t;
  206. uint4 X[4] = {};
  207. if (xrow) {
  208. #pragma unroll
  209. for (int i = 0; i < 4; ++i) X[i] = __ldg(reinterpret_cast<const uint4*>(xrow + k) + i);
  210. }
  211. const int off = j * 64 + t * 16;
  212. const uint4 U0 = *reinterpret_cast<const uint4*>(b + g * kRowStride + off);
  213. const uint4 U8 = *reinterpret_cast<const uint4*>(b + (g + 8) * kRowStride + off);
  214. const uint4 G0 = *reinterpret_cast<const uint4*>(b + (16 + g) * kRowStride + off);
  215. const uint4 G8 = *reinterpret_cast<const uint4*>(b + (24 + g) * kRowStride + off);
  216. const int ru = tile * 16 + g, rg = I + tile * 16 + g;
  217. chunk_mma(cu, U0, U8, scale_pair(sexp, ru, ru + 8, c, C), X);
  218. chunk_mma(cg, G0, G8, scale_pair(sexp, rg, rg + 8, c, C), X);
  219. }
  220. __syncwarp(); // the next issue overwrites this buffer
  221. }
  222. if constexpr (KSPLIT > 1) {
  223. float* red = reinterpret_cast<float*>(smem); // reuses the stage buffers
  224. __syncthreads();
  225. if (ks > 0)
  226. #pragma unroll
  227. for (int i = 0; i < 4; ++i) {
  228. red[((ks - 1) * 32 + lane) * 8 + i] = cg[i];
  229. red[((ks - 1) * 32 + lane) * 8 + 4 + i] = cu[i];
  230. }
  231. __syncthreads();
  232. if (ks > 0) return;
  233. #pragma unroll
  234. for (int s = 0; s < KSPLIT - 1; ++s)
  235. #pragma unroll
  236. for (int i = 0; i < 4; ++i) {
  237. cg[i] += red[(s * 32 + lane) * 8 + i];
  238. cu[i] += red[(s * 32 + lane) * 8 + 4 + i];
  239. }
  240. }
  241. const float a = alpha1[e];
  242. #pragma unroll
  243. for (int i = 0; i < 4; ++i) {
  244. const float gv = fminf(a * cg[i], limit);
  245. const float uv = fminf(fmaxf(a * cu[i], -limit), limit);
  246. const float h = gv / (1.f + expf(-gv)) * uv;
  247. const int row = tile * 16 + g + (i >> 1) * 8, slot = 2 * t + (i & 1);
  248. hbuf[((size_t)u * kSlots + slot) * I + row] = __float2half_rn(h);
  249. }
  250. }
  251. // FC2 for one expert entry and WARPS 16-row tiles (each 16 * I/2 contiguous
  252. // bytes), scaled by alpha2 and the routing weight, into the (token, k) slot.
  253. template <int WARPS>
  254. __global__ void __launch_bounds__(WARPS * 32) fc2_rm(
  255. const uint8_t* __restrict__ w2, const uint8_t* __restrict__ s2,
  256. const float* __restrict__ alpha2, const __half* __restrict__ hbuf,
  257. const float* __restrict__ topk_w, const int* __restrict__ n_entries,
  258. const int* __restrict__ entry_expert, const int* __restrict__ entry_tk,
  259. float* __restrict__ ypart, int Hout, int I) {
  260. const int u = blockIdx.x;
  261. if (u >= *n_entries) return;
  262. const int e = entry_expert[u];
  263. const int warp = threadIdx.x >> 5, lane = threadIdx.x & 31, g = lane >> 2, t = lane & 3;
  264. const int tile = blockIdx.y * WARPS + warp;
  265. if (tile * 16 >= Hout) return;
  266. const int nchunk = I / 128, rowb = I / 2, C = I / 16;
  267. const uint8_t* r0 = w2 + ((size_t)e * Hout + tile * 16 + g) * rowb + 16 * t;
  268. const uint8_t* r8 = r0 + (size_t)8 * rowb;
  269. const uint8_t* sexp = s2 + (size_t)e * Hout * C;
  270. const __half* hrow = hbuf + ((size_t)u * kSlots + g) * I + 32 * t;
  271. constexpr int kMaxChunks = 8; // I <= 1024
  272. uint4 W0[kMaxChunks], W8[kMaxChunks];
  273. uint32_t S[kMaxChunks];
  274. #pragma unroll
  275. for (int k = 0; k < kMaxChunks; ++k)
  276. if (k < nchunk) {
  277. W0[k] = ld_stream(r0 + k * 64); W8[k] = ld_stream(r8 + k * 64);
  278. S[k] = scale_pair(sexp, tile * 16 + g, tile * 16 + g + 8, k * 8 + 2 * t, C);
  279. }
  280. float c[4] = {0.f, 0.f, 0.f, 0.f};
  281. #pragma unroll
  282. for (int k = 0; k < kMaxChunks; ++k)
  283. if (k < nchunk) {
  284. uint4 X[4];
  285. #pragma unroll
  286. for (int i = 0; i < 4; ++i) X[i] = *(reinterpret_cast<const uint4*>(hrow + k * 128) + i);
  287. chunk_mma(c, W0[k], W8[k], S[k], X);
  288. }
  289. const float a = alpha2[e];
  290. #pragma unroll
  291. for (int i = 0; i < 4; ++i) {
  292. const int slot = 2 * t + (i & 1);
  293. const int tk = entry_tk[u * kSlots + slot];
  294. if (tk < 0) continue;
  295. const int row = tile * 16 + g + (i >> 1) * 8;
  296. ypart[(size_t)tk * Hout + row] = a * topk_w[tk] * c[i];
  297. }
  298. }
  299. // out[m] = sum over k of ypart[m, k], in k order, over the pairs with an expert.
  300. __global__ void combine(const float* __restrict__ ypart, const int* __restrict__ topk_ids, int topk,
  301. int Hout, __nv_bfloat16* __restrict__ out) {
  302. const int m = blockIdx.y;
  303. const int f = (blockIdx.x * blockDim.x + threadIdx.x) * 4;
  304. if (f >= Hout) return;
  305. float4 acc = make_float4(0.f, 0.f, 0.f, 0.f);
  306. for (int k = 0; k < topk; ++k) {
  307. if (topk_ids[m * topk + k] < 0) continue;
  308. const float4 v = *reinterpret_cast<const float4*>(ypart + ((size_t)m * topk + k) * Hout + f);
  309. acc.x += v.x; acc.y += v.y; acc.z += v.z; acc.w += v.w;
  310. }
  311. __nv_bfloat162* o = reinterpret_cast<__nv_bfloat162*>(out + (size_t)m * Hout + f);
  312. o[0] = __floats2bfloat162_rn(acc.x, acc.y);
  313. o[1] = __floats2bfloat162_rn(acc.z, acc.w);
  314. }
  315. } // namespace
  316. // w13 [E, 2I, K/2] u8, s13 [E, 2I, K/16] e4m3 (swizzled), alpha1 [E] f32 (the
  317. // weight's global scale), likewise w2/s2/alpha2 with [E, H, I/2]. Scratch is
  318. // passed in (sized for M tokens) so a CUDA graph can hold it.
  319. void megamoe_rm_forward(torch::Tensor x, torch::Tensor topk_ids, torch::Tensor topk_w,
  320. torch::Tensor w13, torch::Tensor s13, torch::Tensor alpha1,
  321. torch::Tensor w2, torch::Tensor s2, torch::Tensor alpha2,
  322. torch::Tensor xh, torch::Tensor ints, torch::Tensor hbuf,
  323. torch::Tensor ypart, torch::Tensor out, double limit, int64_t variant) {
  324. const int M = x.size(0), K = x.size(1), topk = topk_ids.size(1);
  325. const int E = w13.size(0), I = w13.size(1) / 2, Hout = w2.size(0) ? w2.size(1) : 0;
  326. const int pairs = M * topk;
  327. TORCH_CHECK(I <= 1024 && I % 128 == 0 && K % (128 * 8) == 0 && Hout % 128 == 0 && pairs <= 4096 && E <= 1024);
  328. TORCH_CHECK(w13.size(2) == K / 2 && w2.size(2) == I / 2);
  329. auto stream = at::cuda::getCurrentCUDAStream();
  330. int* n_entries = ints.data_ptr<int>();
  331. int* entry_expert = n_entries + 1;
  332. int* entry_tk = entry_expert + pairs;
  333. route_prep<<<1, 1024, (2 * E + 32) * sizeof(int), stream>>>(
  334. topk_ids.data_ptr<int>(), M, topk, E, reinterpret_cast<const __nv_bfloat16*>(x.data_ptr()), K,
  335. reinterpret_cast<__half*>(xh.data_ptr()), n_entries, entry_expert, entry_tk);
  336. const uint8_t* s13p = reinterpret_cast<const uint8_t*>(s13.data_ptr());
  337. const uint8_t* s2p = reinterpret_cast<const uint8_t*>(s2.data_ptr());
  338. auto fc1 = [&](auto ksplit) {
  339. constexpr int KS = decltype(ksplit)::value;
  340. const size_t smem = (size_t)KS * 2 * kStageBytes;
  341. cudaFuncSetAttribute(fc1_rm<KS>, cudaFuncAttributeMaxDynamicSharedMemorySize, (int)smem);
  342. fc1_rm<KS><<<dim3(pairs, I / 16), KS * 32, smem, stream>>>(
  343. w13.data_ptr<uint8_t>(), s13p, alpha1.data_ptr<float>(),
  344. reinterpret_cast<const __half*>(xh.data_ptr()), topk, n_entries, entry_expert, entry_tk,
  345. reinterpret_cast<__half*>(hbuf.data_ptr()), K, I, (float)limit);
  346. };
  347. auto fc2 = [&](auto warps) {
  348. constexpr int W = decltype(warps)::value;
  349. fc2_rm<W><<<dim3(pairs, (Hout / 16 + W - 1) / W), W * 32, 0, stream>>>(
  350. w2.data_ptr<uint8_t>(), s2p, alpha2.data_ptr<float>(),
  351. reinterpret_cast<const __half*>(hbuf.data_ptr()), topk_w.data_ptr<float>(), n_entries,
  352. entry_expert, entry_tk, ypart.data_ptr<float>(), Hout, I);
  353. };
  354. using std::integral_constant;
  355. switch (variant / 10) { // tens digit: FC1 K split
  356. case 0: fc1(integral_constant<int, 4>{}); break;
  357. case 1: fc1(integral_constant<int, 2>{}); break;
  358. default: fc1(integral_constant<int, 8>{}); break;
  359. }
  360. switch (variant % 10) { // units digit: FC2 warps per block
  361. case 0: fc2(integral_constant<int, 4>{}); break;
  362. case 1: fc2(integral_constant<int, 2>{}); break;
  363. case 2: fc2(integral_constant<int, 8>{}); break;
  364. default: fc2(integral_constant<int, 1>{}); break;
  365. }
  366. combine<<<dim3((Hout / 4 + 127) / 128, M), 128, 0, stream>>>(
  367. ypart.data_ptr<float>(), topk_ids.data_ptr<int>(), topk, Hout,
  368. reinterpret_cast<__nv_bfloat16*>(out.data_ptr()));
  369. }
  370. PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { m.def("forward", &megamoe_rm_forward); }