moe_prefill.cu 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. // SPDX-License-Identifier: Apache-2.0
  2. // Prefill MoE for GB10 (sm_121a): NVFP4 x NVFP4 grouped GEMMs on the tensors
  3. // vLLM hands FlashInfer's CUTLASS MoE.
  4. // fc1: gathers token rows by index, SwiGLU and NVFP4 requantization in the
  5. // epilogue. xq [Mtok, K/2] u8 (e2m1 pairs, low nibble first) with
  6. // 128x4-swizzled e4m3 scales xs; w13 [E, 2I, K/2] (rows 0..I-1 up,
  7. // I..2I-1 gate). Writes hq [R, I/2] in expert-sorted row order, hs
  8. // swizzled over R.
  9. // fc2: hq x w2 [E, H, I/2], scaled by g2, to bf16 rows y [R, H].
  10. // finalize: out[t] = sum_k w[t, k] * y[pos[t, k]].
  11. // Rows are grouped by expert (expert_off) and cut into 128-row tiles; tiles
  12. // [T, 2] lists (expert, m tile) and may end in padding entries with expert >= E.
  13. #include <torch/extension.h>
  14. #include <ATen/cuda/CUDAContext.h>
  15. #include <cuda_bf16.h>
  16. #include <cuda_fp8.h>
  17. namespace {
  18. #ifndef MOE_BKB
  19. #define MOE_BKB 64
  20. #endif
  21. #ifndef MOE_STAGES
  22. #define MOE_STAGES 4
  23. #endif
  24. #ifndef MOE_PREFETCH
  25. #define MOE_PREFETCH 0 // bit 0: bulk-prefetch the A rows to L2 at tile start; bit 1: the B rows
  26. #endif
  27. #ifndef MOE_MINB
  28. #define MOE_MINB 1
  29. #endif
  30. constexpr int BM = 128, BN = 128, BKB = MOE_BKB; // rows, weight rows, bytes of K per stage
  31. constexpr int STAGES = MOE_STAGES, THREADS = 256;
  32. constexpr int CPR = BKB / 16; // 16-byte chunks per smem row
  33. constexpr int NCP = BM * CPR / THREADS; // chunks per thread per operand
  34. constexpr int WPR = BKB / 32; // 4-byte scale words (64 elements) per row per stage
  35. constexpr int NSW = BM * WPR / THREADS; // scale words per thread per operand
  36. constexpr int A_BYTES = BM * BKB, B_BYTES = BN * BKB, SA_BYTES = BM * WPR * 4, SB_BYTES = BN * WPR * 4;
  37. constexpr int STAGE_BYTES = A_BYTES + B_BYTES + SA_BYTES + SB_BYTES;
  38. __device__ __forceinline__ size_t scale_off(int r, int c, int C) { // flashinfer's 128x4 swizzle
  39. return ((size_t)(r >> 7) * (C >> 2) + (c >> 2)) * 512 + (r & 31) * 16 + ((r >> 5) & 3) * 4 + (c & 3);
  40. }
  41. __device__ __forceinline__ uint32_t smem_u32(const void* p) { return (uint32_t)__cvta_generic_to_shared(p); }
  42. __device__ __forceinline__ void cp16(void* dst, const void* src, bool ok) {
  43. asm volatile("cp.async.cg.shared.global [%0], [%1], 16, %2;\n" ::"r"(smem_u32(dst)), "l"(src), "r"(ok ? 16 : 0));
  44. }
  45. __device__ __forceinline__ void cp4(void* dst, const void* src, bool ok) {
  46. asm volatile("cp.async.ca.shared.global [%0], [%1], 4, %2;\n" ::"r"(smem_u32(dst)), "l"(src), "r"(ok ? 4 : 0));
  47. }
  48. __device__ __forceinline__ void cp_commit() { asm volatile("cp.async.commit_group;\n"); }
  49. template <int N>
  50. __device__ __forceinline__ void cp_wait() { asm volatile("cp.async.wait_group %0;\n" ::"n"(N)); }
  51. __device__ __forceinline__ void ldsm4(uint32_t& r0, uint32_t& r1, uint32_t& r2, uint32_t& r3, const void* p) {
  52. asm volatile("ldmatrix.sync.aligned.m8n8.x4.shared.b16 {%0,%1,%2,%3}, [%4];\n"
  53. : "=r"(r0), "=r"(r1), "=r"(r2), "=r"(r3) : "r"(smem_u32(p)));
  54. }
  55. __device__ __forceinline__ void mma_fp4(float (&d)[4], const uint32_t (&a)[4], uint32_t b0, uint32_t b1, uint32_t sa,
  56. uint32_t sb) {
  57. const uint16_t z = 0;
  58. asm volatile(
  59. "mma.sync.aligned.kind::mxf4nvf4.block_scale.scale_vec::4X.m16n8k64.row.col.f32.e2m1.e2m1.f32.ue4m3 "
  60. "{%0,%1,%2,%3}, {%4,%5,%6,%7}, {%8,%9}, {%0,%1,%2,%3}, {%10}, {%11,%12}, {%13}, {%14,%15};\n"
  61. : "+f"(d[0]), "+f"(d[1]), "+f"(d[2]), "+f"(d[3])
  62. : "r"(a[0]), "r"(a[1]), "r"(a[2]), "r"(a[3]), "r"(b0), "r"(b1), "r"(sa), "h"(z), "h"(z), "r"(sb), "h"(z),
  63. "h"(z));
  64. }
  65. // 16-byte chunk c of smem row r, XOR-swizzled so ldmatrix is conflict-free.
  66. __device__ __forceinline__ int swz(int r, int c) {
  67. return r * BKB + ((CPR == 4 ? (c ^ ((r >> 1) & 3)) : (c ^ (r & 7))) << 4);
  68. }
  69. __device__ __forceinline__ uint8_t e2m1x2(float lo, float hi) {
  70. uint16_t r; // hi goes to the upper nibble
  71. asm("{ .reg .b8 t; cvt.rn.satfinite.e2m1x2.f32 t, %2, %1; mov.b16 %0, {t, t}; }\n" : "=h"(r) : "f"(lo), "f"(hi));
  72. return (uint8_t)(r & 0xff);
  73. }
  74. struct Args {
  75. const uint8_t* a; // fc1: xq, fc2: hq
  76. const uint8_t* as; // their swizzled scales
  77. const int* rows; // fc1: token of each expanded row
  78. const int* expert_off; // [E+1]
  79. const int2* tiles; // [T] (expert, m tile)
  80. const uint8_t* w; // w13 or w2
  81. const uint8_t* ws; // their per-expert swizzled scales
  82. const float* alpha; // g13 or g2 [E]
  83. const float* a2; // fc1: fc2's input global scale [E]
  84. uint8_t* hq; // fc1 outputs
  85. uint8_t* hs;
  86. __nv_bfloat16* y; // fc2 output (bf16), or
  87. uint8_t* y8; // e4m3 rows with
  88. float* y8s; // one scale per (row, 128 columns)
  89. int K, N, I, E; // K of this GEMM, weight rows per expert, intermediate size
  90. float limit;
  91. };
  92. // FC1: B tile = 64 up rows + the 64 matching gate rows; each warp holds 32 up
  93. // and the same 32 gate columns. FC2: B tile = 128 contiguous rows of w2.
  94. template <bool FC1>
  95. __global__ void __launch_bounds__(THREADS, MOE_MINB) moe_gemm(const Args p) {
  96. extern __shared__ __align__(128) uint8_t smem[];
  97. const int2 tile = p.tiles[blockIdx.y];
  98. if (tile.x >= p.E) return;
  99. const int e = tile.x;
  100. const int n0 = blockIdx.x * (FC1 ? BN / 2 : BN);
  101. const int r_begin = p.expert_off[e] + tile.y * BM, r_end = p.expert_off[e + 1];
  102. const int tid = threadIdx.x, lane = tid & 31, warp = tid >> 5, wm = warp & 3, wn = warp >> 2;
  103. const int KB = p.K / 2, KS = p.K / 16;
  104. const uint8_t* wexp = p.w + (size_t)e * p.N * KB;
  105. const uint8_t* sexp = p.ws + (size_t)e * p.N * KS;
  106. auto wrow_of = [&](int r) { return FC1 ? (r < BN / 2 ? n0 + r : p.I + n0 + r - BN / 2) : n0 + r; };
  107. const uint8_t* a_src[NCP]; bool a_ok[NCP]; int a_dst[NCP];
  108. const uint8_t* b_src[NCP];
  109. #pragma unroll
  110. for (int j = 0; j < NCP; ++j) {
  111. const int idx = tid + j * THREADS, r = idx / CPR, c = idx % CPR;
  112. const int gr = r_begin + r;
  113. a_ok[j] = gr < r_end;
  114. const int arow = a_ok[j] ? (FC1 ? p.rows[gr] : gr) : 0;
  115. a_src[j] = p.a + (size_t)arow * KB + c * 16;
  116. a_dst[j] = swz(r, c);
  117. b_src[j] = wexp + (size_t)wrow_of(r) * KB + c * 16;
  118. }
  119. if constexpr ((MOE_PREFETCH & 1) != 0) {
  120. // One whole-row request per gathered row: DRAM serves 2 KB runs instead of
  121. // 64-byte pieces spread over the K loop. Bit 2: only the first N tile asks.
  122. // Bits 3+: ask for the rows of the tile that many tiles ahead instead.
  123. constexpr int AHEAD = MOE_PREFETCH >> 3;
  124. const bool asker = (MOE_PREFETCH & 4) == 0 || blockIdx.x == 0;
  125. const int ty = blockIdx.y + AHEAD;
  126. if (asker && tid < BM && ty < gridDim.y) {
  127. const int2 pt = p.tiles[ty];
  128. if (pt.x < p.E) {
  129. const int pr = p.expert_off[pt.x] + pt.y * BM + tid;
  130. if (pr < p.expert_off[pt.x + 1]) {
  131. const uint8_t* src = p.a + (size_t)(FC1 ? p.rows[pr] : pr) * KB;
  132. asm volatile("cp.async.bulk.prefetch.L2.global [%0], %1;\n" ::"l"(src), "r"(KB));
  133. }
  134. }
  135. }
  136. }
  137. if constexpr ((MOE_PREFETCH & 2) != 0) {
  138. if (tid >= BM && tid < BM + BN) {
  139. asm volatile("cp.async.bulk.prefetch.L2.global [%0], %1;\n" ::"l"(wexp + (size_t)wrow_of(tid - BM) * KB), "r"(KB));
  140. }
  141. }
  142. int s_r[NSW], s_w[NSW], a_srow[NSW], b_srow[NSW]; bool sa_ok[NSW];
  143. #pragma unroll
  144. for (int j = 0; j < NSW; ++j) {
  145. const int idx = tid + j * THREADS;
  146. s_r[j] = idx / WPR; s_w[j] = idx % WPR;
  147. sa_ok[j] = r_begin + s_r[j] < r_end;
  148. a_srow[j] = sa_ok[j] ? (FC1 ? p.rows[r_begin + s_r[j]] : r_begin + s_r[j]) : 0;
  149. b_srow[j] = wrow_of(s_r[j]);
  150. }
  151. auto load_stage = [&](int s, int kt) {
  152. uint8_t* st = smem + s * STAGE_BYTES;
  153. const int kb = kt * BKB;
  154. #pragma unroll
  155. for (int j = 0; j < NCP; ++j) {
  156. cp16(st + a_dst[j], a_src[j] + kb, a_ok[j]);
  157. cp16(st + A_BYTES + a_dst[j], b_src[j] + kb, true);
  158. }
  159. #pragma unroll
  160. for (int j = 0; j < NSW; ++j) {
  161. const int c = kt * (BKB / 8) + s_w[j] * 4;
  162. cp4(st + A_BYTES + B_BYTES + (s_r[j] * WPR + s_w[j]) * 4, p.as + scale_off(a_srow[j], c, KS), sa_ok[j]);
  163. cp4(st + A_BYTES + B_BYTES + SA_BYTES + (s_r[j] * WPR + s_w[j]) * 4, sexp + scale_off(b_srow[j], c, KS), true);
  164. }
  165. };
  166. float acc[2][8][4];
  167. #pragma unroll
  168. for (int i = 0; i < 2; ++i)
  169. #pragma unroll
  170. for (int j = 0; j < 8; ++j)
  171. #pragma unroll
  172. for (int v = 0; v < 4; ++v) acc[i][j][v] = 0.f;
  173. const int KT = KB / BKB;
  174. #pragma unroll
  175. for (int s = 0; s < STAGES - 1; ++s) {
  176. if (s < KT) load_stage(s, s);
  177. cp_commit();
  178. }
  179. const int g = lane >> 2, q = lane >> 3, rr = lane & 7;
  180. for (int kt = 0; kt < KT; ++kt) {
  181. cp_wait<STAGES - 2>();
  182. __syncthreads();
  183. if (kt + STAGES - 1 < KT) load_stage((kt + STAGES - 1) % STAGES, kt + STAGES - 1);
  184. cp_commit();
  185. const uint8_t* st = smem + (kt % STAGES) * STAGE_BYTES;
  186. const uint8_t* sA = st;
  187. const uint8_t* sB = st + A_BYTES;
  188. const uint32_t* sSA = reinterpret_cast<const uint32_t*>(st + A_BYTES + B_BYTES);
  189. const uint32_t* sSB = reinterpret_cast<const uint32_t*>(st + A_BYTES + B_BYTES + SA_BYTES);
  190. #pragma unroll
  191. for (int kk = 0; kk < BKB / 32; ++kk) {
  192. uint32_t af[2][4], sa[2];
  193. #pragma unroll
  194. for (int mi = 0; mi < 2; ++mi) {
  195. const int r = wm * 32 + mi * 16 + rr + (q & 1) * 8;
  196. ldsm4(af[mi][0], af[mi][1], af[mi][2], af[mi][3], sA + swz(r, kk * 2 + (q >> 1)));
  197. sa[mi] = sSA[(wm * 32 + mi * 16 + g + 8 * (lane & 1)) * WPR + kk];
  198. }
  199. #pragma unroll
  200. for (int np = 0; np < 4; ++np) { // pairs of n8 blocks, j = 2np and 2np+1
  201. const int nb = FC1 ? (np < 2 ? 0 : BN / 2) + wn * 32 + (np & 1) * 16 : wn * 64 + np * 16;
  202. uint32_t b[4];
  203. ldsm4(b[0], b[1], b[2], b[3], sB + swz(nb + (q >> 1) * 8 + rr, kk * 2 + (q & 1)));
  204. const uint32_t sb0 = sSB[(nb + g) * WPR + kk], sb1 = sSB[(nb + 8 + g) * WPR + kk];
  205. #pragma unroll
  206. for (int mi = 0; mi < 2; ++mi) {
  207. mma_fp4(acc[mi][np * 2], af[mi], b[0], b[1], sa[mi], sb0);
  208. mma_fp4(acc[mi][np * 2 + 1], af[mi], b[2], b[3], sa[mi], sb1);
  209. }
  210. }
  211. }
  212. }
  213. cp_wait<0>();
  214. const float ga = p.alpha[e];
  215. const int t4 = lane & 3;
  216. if constexpr (FC1) {
  217. // acc[mi][j] (j<4) is up, acc[mi][j+4] the matching gate. Intermediate column
  218. // = n0 + wn*32 + j*8 + 2t + {0,1}; rows g and g+8 of each m16 tile.
  219. const float qs = p.a2[e];
  220. const int IB = p.I / 2, IS = p.I / 16;
  221. #pragma unroll
  222. for (int mi = 0; mi < 2; ++mi)
  223. #pragma unroll
  224. for (int half = 0; half < 2; ++half) {
  225. const int row = r_begin + wm * 32 + mi * 16 + g + half * 8;
  226. #pragma unroll
  227. for (int blk = 0; blk < 2; ++blk) { // two 16-column quant blocks per warp
  228. float h[4];
  229. #pragma unroll
  230. for (int jj = 0; jj < 2; ++jj)
  231. #pragma unroll
  232. for (int v = 0; v < 2; ++v) {
  233. const int j = blk * 2 + jj;
  234. const float gv = fminf(ga * acc[mi][j + 4][half * 2 + v], p.limit);
  235. const float uv = fminf(fmaxf(ga * acc[mi][j][half * 2 + v], -p.limit), p.limit);
  236. h[jj * 2 + v] = gv / (1.f + __expf(-gv)) * uv;
  237. }
  238. float amax = fmaxf(fmaxf(fabsf(h[0]), fabsf(h[1])), fmaxf(fabsf(h[2]), fabsf(h[3])));
  239. amax = fmaxf(amax, __shfl_xor_sync(0xffffffff, amax, 1));
  240. amax = fmaxf(amax, __shfl_xor_sync(0xffffffff, amax, 2));
  241. const __nv_fp8_e4m3 sf8(amax * qs * (1.f / 6.f));
  242. const float sf = float(sf8);
  243. const float inv = sf > 0.f ? qs / sf : 0.f;
  244. const uint32_t b0 = e2m1x2(h[0] * inv, h[1] * inv), b1 = e2m1x2(h[2] * inv, h[3] * inv);
  245. // Thread t holds byte t of each 4-byte word (columns j*8 .. j*8+7).
  246. uint32_t w0 = b0 << (8 * t4), w1 = b1 << (8 * t4);
  247. w0 |= __shfl_xor_sync(0xffffffff, w0, 1); w0 |= __shfl_xor_sync(0xffffffff, w0, 2);
  248. w1 |= __shfl_xor_sync(0xffffffff, w1, 1); w1 |= __shfl_xor_sync(0xffffffff, w1, 2);
  249. const int col = n0 + wn * 32 + blk * 16;
  250. if (row < r_end) {
  251. if (t4 == 0) *reinterpret_cast<uint32_t*>(p.hq + (size_t)row * IB + col / 2) = w0;
  252. if (t4 == 1) *reinterpret_cast<uint32_t*>(p.hq + (size_t)row * IB + col / 2 + 4) = w1;
  253. if (t4 == 2) p.hs[scale_off(row, col / 16, IS)] = sf8.__x;
  254. }
  255. }
  256. }
  257. } else {
  258. // Stage the 128x128 bf16 tile in smem, then write whole 16-byte chunks.
  259. constexpr int YS = BN + 8; // padded row stride (bf16 elements)
  260. __nv_bfloat16* sy = reinterpret_cast<__nv_bfloat16*>(smem);
  261. __syncthreads();
  262. #pragma unroll
  263. for (int mi = 0; mi < 2; ++mi)
  264. #pragma unroll
  265. for (int half = 0; half < 2; ++half) {
  266. const int r = wm * 32 + mi * 16 + g + half * 8;
  267. #pragma unroll
  268. for (int j = 0; j < 8; ++j)
  269. *reinterpret_cast<__nv_bfloat162*>(sy + r * YS + wn * 64 + j * 8 + 2 * t4) =
  270. __floats2bfloat162_rn(ga * acc[mi][j][half * 2], ga * acc[mi][j][half * 2 + 1]);
  271. }
  272. __syncthreads();
  273. const int H = p.N;
  274. if (p.y8 != nullptr) {
  275. // 8 warps x 16 rows: each warp reduces a row's amax, then writes e4m3 in 16-byte chunks.
  276. for (int r = warp; r < BM; r += THREADS / 32) {
  277. if (r_begin + r >= r_end) break;
  278. const __nv_bfloat162 v2 = *reinterpret_cast<const __nv_bfloat162*>(sy + r * YS + lane * 4);
  279. const __nv_bfloat162 w2v = *reinterpret_cast<const __nv_bfloat162*>(sy + r * YS + lane * 4 + 2);
  280. const float2 f0 = __bfloat1622float2(v2), f1 = __bfloat1622float2(w2v);
  281. float amax = fmaxf(fmaxf(fabsf(f0.x), fabsf(f0.y)), fmaxf(fabsf(f1.x), fabsf(f1.y)));
  282. #pragma unroll
  283. for (int o = 16; o > 0; o >>= 1) amax = fmaxf(amax, __shfl_xor_sync(0xffffffff, amax, o));
  284. const float sc = amax > 0.f ? amax / 448.f : 1.f, inv = 1.f / sc;
  285. const __nv_fp8x4_e4m3 q4(make_float4(f0.x * inv, f0.y * inv, f1.x * inv, f1.y * inv));
  286. *reinterpret_cast<uint32_t*>(p.y8 + (size_t)(r_begin + r) * H + n0 + lane * 4) =
  287. *reinterpret_cast<const uint32_t*>(&q4);
  288. if (lane == 0) p.y8s[(size_t)(r_begin + r) * (H / BN) + n0 / BN] = sc;
  289. }
  290. } else {
  291. #pragma unroll
  292. for (int i = 0; i < BM * BN / 8 / THREADS; ++i) {
  293. const int idx = tid + i * THREADS, r = idx / (BN / 8), c = idx % (BN / 8);
  294. if (r_begin + r < r_end)
  295. *reinterpret_cast<uint4*>(p.y + (size_t)(r_begin + r) * H + n0 + c * 8) =
  296. *reinterpret_cast<const uint4*>(sy + r * YS + c * 8);
  297. }
  298. }
  299. }
  300. }
  301. // out[t] = sum_k w[t, k] * y8[pos[t, k]] * y8s[pos[t, k], column / 128].
  302. __global__ void finalize8_kernel(const uint8_t* __restrict__ y8, const float* __restrict__ y8s,
  303. const int* __restrict__ pos, const float* __restrict__ w,
  304. __nv_bfloat16* __restrict__ out, int H, int topk) {
  305. const int t = blockIdx.x;
  306. __shared__ int ps[16];
  307. __shared__ float ws[16];
  308. if (threadIdx.x < topk) {
  309. ps[threadIdx.x] = pos[t * topk + threadIdx.x];
  310. ws[threadIdx.x] = w[t * topk + threadIdx.x];
  311. }
  312. __syncthreads();
  313. for (int c = threadIdx.x * 16; c < H; c += blockDim.x * 16) {
  314. float s[16];
  315. #pragma unroll
  316. for (int i = 0; i < 16; ++i) s[i] = 0.f;
  317. for (int k = 0; k < topk; ++k) {
  318. const uint4 v = *reinterpret_cast<const uint4*>(y8 + (size_t)ps[k] * H + c);
  319. const float f = ws[k] * y8s[(size_t)ps[k] * (H / BN) + c / BN];
  320. const __nv_fp8x4_e4m3* q = reinterpret_cast<const __nv_fp8x4_e4m3*>(&v);
  321. #pragma unroll
  322. for (int i = 0; i < 4; ++i) {
  323. const float4 d = static_cast<float4>(q[i]);
  324. s[4 * i] += f * d.x; s[4 * i + 1] += f * d.y; s[4 * i + 2] += f * d.z; s[4 * i + 3] += f * d.w;
  325. }
  326. }
  327. uint4 o[2];
  328. __nv_bfloat162* ob = reinterpret_cast<__nv_bfloat162*>(o);
  329. #pragma unroll
  330. for (int i = 0; i < 8; ++i) ob[i] = __floats2bfloat162_rn(s[2 * i], s[2 * i + 1]);
  331. *reinterpret_cast<uint4*>(out + (size_t)t * H + c) = o[0];
  332. *reinterpret_cast<uint4*>(out + (size_t)t * H + c + 8) = o[1];
  333. }
  334. }
  335. // One block per token: out[t] = sum_k w[t, k] * y[pos[t*topk + k]], fp32 accumulation.
  336. __global__ void finalize_kernel(const __nv_bfloat16* __restrict__ y, const int* __restrict__ pos,
  337. const float* __restrict__ w, __nv_bfloat16* __restrict__ out, int H, int topk) {
  338. const int t = blockIdx.x;
  339. __shared__ int ps[16];
  340. __shared__ float ws[16];
  341. if (threadIdx.x < topk) {
  342. ps[threadIdx.x] = pos[t * topk + threadIdx.x];
  343. ws[threadIdx.x] = w[t * topk + threadIdx.x];
  344. }
  345. __syncthreads();
  346. for (int c = threadIdx.x * 8; c < H; c += blockDim.x * 8) {
  347. float s[8] = {0, 0, 0, 0, 0, 0, 0, 0};
  348. for (int k = 0; k < topk; ++k) {
  349. const uint4 v = *reinterpret_cast<const uint4*>(y + (size_t)ps[k] * H + c);
  350. const __nv_bfloat162* b = reinterpret_cast<const __nv_bfloat162*>(&v);
  351. #pragma unroll
  352. for (int i = 0; i < 4; ++i) {
  353. const float2 f = __bfloat1622float2(b[i]);
  354. s[2 * i] += ws[k] * f.x;
  355. s[2 * i + 1] += ws[k] * f.y;
  356. }
  357. }
  358. uint4 o;
  359. __nv_bfloat162* ob = reinterpret_cast<__nv_bfloat162*>(&o);
  360. #pragma unroll
  361. for (int i = 0; i < 4; ++i) ob[i] = __floats2bfloat162_rn(s[2 * i], s[2 * i + 1]);
  362. *reinterpret_cast<uint4*>(out + (size_t)t * H + c) = o;
  363. }
  364. }
  365. template <bool FC1>
  366. void launch(const Args& a, int n_tiles_n, int n_tiles) {
  367. static bool attr = false;
  368. if (!attr) {
  369. cudaFuncSetAttribute(moe_gemm<FC1>, cudaFuncAttributeMaxDynamicSharedMemorySize, STAGES * STAGE_BYTES);
  370. attr = true;
  371. }
  372. moe_gemm<FC1><<<dim3(n_tiles_n, n_tiles), THREADS, STAGES * STAGE_BYTES, at::cuda::getCurrentCUDAStream()>>>(a);
  373. }
  374. } // namespace
  375. void fc1(torch::Tensor xq, torch::Tensor xs, torch::Tensor rows, torch::Tensor expert_off, torch::Tensor tiles,
  376. torch::Tensor w13, torch::Tensor s13, torch::Tensor g13, torch::Tensor a2, torch::Tensor hq, torch::Tensor hs,
  377. double limit) {
  378. Args a{};
  379. a.a = xq.data_ptr<uint8_t>(); a.as = xs.data_ptr<uint8_t>(); a.rows = rows.data_ptr<int>();
  380. a.expert_off = expert_off.data_ptr<int>(); a.tiles = reinterpret_cast<const int2*>(tiles.data_ptr<int>());
  381. a.w = w13.data_ptr<uint8_t>(); a.ws = s13.data_ptr<uint8_t>(); a.alpha = g13.data_ptr<float>(); a.a2 = a2.data_ptr<float>();
  382. a.hq = hq.data_ptr<uint8_t>(); a.hs = hs.data_ptr<uint8_t>();
  383. a.K = xq.size(1) * 2; a.N = w13.size(1); a.I = w13.size(1) / 2; a.E = w13.size(0); a.limit = (float)limit;
  384. TORCH_CHECK(a.I % (BN / 2) == 0 && a.K % (BKB * 2) == 0);
  385. launch<true>(a, a.I / (BN / 2), tiles.size(0));
  386. }
  387. void fc2(torch::Tensor hq, torch::Tensor hs, torch::Tensor expert_off, torch::Tensor tiles, torch::Tensor w2,
  388. torch::Tensor s2, torch::Tensor g2, torch::Tensor y, c10::optional<torch::Tensor> y8s) {
  389. Args a{};
  390. a.a = hq.data_ptr<uint8_t>(); a.as = hs.data_ptr<uint8_t>();
  391. a.expert_off = expert_off.data_ptr<int>(); a.tiles = reinterpret_cast<const int2*>(tiles.data_ptr<int>());
  392. a.w = w2.data_ptr<uint8_t>(); a.ws = s2.data_ptr<uint8_t>(); a.alpha = g2.data_ptr<float>();
  393. if (y8s.has_value()) { // y is e4m3 [R, H] as u8
  394. a.y8 = y.data_ptr<uint8_t>(); a.y8s = y8s->data_ptr<float>();
  395. } else {
  396. a.y = reinterpret_cast<__nv_bfloat16*>(y.data_ptr());
  397. }
  398. a.K = hq.size(1) * 2; a.N = w2.size(1); a.I = a.K; a.E = w2.size(0);
  399. TORCH_CHECK(a.N % BN == 0 && a.K % (BKB * 2) == 0);
  400. launch<false>(a, a.N / BN, tiles.size(0));
  401. }
  402. void finalize(torch::Tensor y, torch::Tensor pos, torch::Tensor w, torch::Tensor out) {
  403. const int T = out.size(0), H = out.size(1), topk = pos.numel() / T;
  404. TORCH_CHECK(H % 8 == 0 && topk <= 16);
  405. finalize_kernel<<<T, 128, 0, at::cuda::getCurrentCUDAStream()>>>(
  406. reinterpret_cast<const __nv_bfloat16*>(y.data_ptr()), pos.data_ptr<int>(), w.data_ptr<float>(),
  407. reinterpret_cast<__nv_bfloat16*>(out.data_ptr()), H, topk);
  408. }
  409. void finalize8(torch::Tensor y8, torch::Tensor y8s, torch::Tensor pos, torch::Tensor w, torch::Tensor out) {
  410. const int T = out.size(0), H = out.size(1), topk = pos.numel() / T;
  411. TORCH_CHECK(H % 16 == 0 && topk <= 16);
  412. finalize8_kernel<<<T, 128, 0, at::cuda::getCurrentCUDAStream()>>>(
  413. y8.data_ptr<uint8_t>(), y8s.data_ptr<float>(), pos.data_ptr<int>(), w.data_ptr<float>(),
  414. reinterpret_cast<__nv_bfloat16*>(out.data_ptr()), H, topk);
  415. }
  416. PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
  417. m.def("finalize8", &finalize8);
  418. m.def("fc1", &fc1);
  419. m.def("fc2", &fc2, py::arg("hq"), py::arg("hs"), py::arg("expert_off"), py::arg("tiles"), py::arg("w2"), py::arg("s2"), py::arg("g2"), py::arg("y"), py::arg("y8s") = py::none());
  420. m.def("finalize", &finalize);
  421. }