megadense4.cu 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. // SPDX-License-Identifier: Apache-2.0
  2. // megadense4: W4A16 dense GEMM for decode-sized batches on GB10 (sm_121a),
  3. // y[m][n] = gscale * sum_k x[m][k] w[n][k], M <= 64, and the dequantization
  4. // that larger batches use to hand the weight to cuBLAS.
  5. //
  6. // Weights are NVFP4 (e2m1 values, e4m3 scale per 16 k, one fp32 global scale)
  7. // that dense_fp8.py quantizes at load and stores in megamoe.cu's tiled layout:
  8. // w [N/16][K/128][half][lane][16 B], half 0 row g and half 1 row g+8 of the
  9. // tile (lane = 4g + t), lane t holding bytes 64c + 16t.. of the row, i.e.
  10. // k 32t..32t+31 of chunk c, low nibble first;
  11. // s [N/16][K/128][lane] u32 = {row g: blocks 2t, 2t+1; row g+8: same}.
  12. // A warp's load is then 512 contiguous bytes, which is what reaches 230 GB/s.
  13. // The math is megamoe.cu's chunk_mma: fp4 x e4m3 is exact in fp16, and x
  14. // stays 16-bit.
  15. #include <ATen/cuda/CUDAContext.h>
  16. #include <cuda_bf16.h>
  17. #include <cuda_fp16.h>
  18. #include <torch/extension.h>
  19. namespace {
  20. __device__ __forceinline__ uint4 ld_stream(const void* p) {
  21. uint4 v;
  22. asm volatile("ld.global.nc.L1::no_allocate.v4.u32 {%0,%1,%2,%3}, [%4];"
  23. : "=r"(v.x), "=r"(v.y), "=r"(v.z), "=r"(v.w)
  24. : "l"(p));
  25. return v;
  26. }
  27. __device__ __forceinline__ uint32_t e2m1x2(uint32_t byte) {
  28. uint32_t r;
  29. asm("{ .reg .b8 t; cvt.u8.u32 t, %1; cvt.rn.f16x2.e2m1x2 %0, t; }" : "=r"(r) : "r"(byte));
  30. return r;
  31. }
  32. __device__ __forceinline__ uint32_t e4m3x2(uint32_t two) {
  33. uint32_t r;
  34. asm("{ .reg .b16 t; cvt.u16.u32 t, %1; cvt.rn.f16x2.e4m3x2 %0, t; }" : "=r"(r) : "r"(two));
  35. return r;
  36. }
  37. __device__ __forceinline__ uint32_t hmul2(uint32_t a, uint32_t b) {
  38. __half2 r = __hmul2(*reinterpret_cast<__half2*>(&a), *reinterpret_cast<__half2*>(&b));
  39. return *reinterpret_cast<uint32_t*>(&r);
  40. }
  41. __device__ __forceinline__ uint32_t bcast(uint32_t h2, int hi) {
  42. uint32_t v = hi ? (h2 >> 16) : (h2 & 0xffff);
  43. return v | (v << 16);
  44. }
  45. __device__ __forceinline__ void mma16816(float c[4], const uint32_t a[4], uint32_t b0, uint32_t b1) {
  46. asm volatile(
  47. "mma.sync.aligned.m16n8k16.row.col.f32.f16.f16.f32 "
  48. "{%0,%1,%2,%3}, {%4,%5,%6,%7}, {%8,%9}, {%0,%1,%2,%3};"
  49. : "+f"(c[0]), "+f"(c[1]), "+f"(c[2]), "+f"(c[3])
  50. : "r"(a[0]), "r"(a[1]), "r"(a[2]), "r"(a[3]), "r"(b0), "r"(b1));
  51. }
  52. __device__ __forceinline__ uint32_t word(const uint4& v, int i) {
  53. return i == 0 ? v.x : i == 1 ? v.y : i == 2 ? v.z : v.w;
  54. }
  55. __device__ __forceinline__ uint4 bf16x8_to_f16x8(uint4 v) {
  56. uint32_t w[4] = {v.x, v.y, v.z, v.w};
  57. #pragma unroll
  58. for (int i = 0; i < 4; ++i) {
  59. const float2 f = __bfloat1622float2(*reinterpret_cast<__nv_bfloat162*>(&w[i]));
  60. __half2 h = __float22half2_rn(f);
  61. w[i] = *reinterpret_cast<uint32_t*>(&h);
  62. }
  63. return make_uint4(w[0], w[1], w[2], w[3]);
  64. }
  65. // The dequantized A fragments of mma step j for rows g and g+8.
  66. __device__ __forceinline__ void a_frag(uint32_t a[4], const uint4& w0, const uint4& w1, uint32_t sc, int j) {
  67. const uint32_t s_lo = e4m3x2(sc & 0xffff), s_hi = e4m3x2(sc >> 16);
  68. const int blk = j >> 2;
  69. const uint32_t sg = bcast(s_lo, blk), sg8 = bcast(s_hi, blk);
  70. const uint32_t p0 = (word(w0, j >> 1) >> ((j & 1) * 16)) & 0xffff;
  71. const uint32_t p1 = (word(w1, j >> 1) >> ((j & 1) * 16)) & 0xffff;
  72. a[0] = hmul2(e2m1x2(p0 & 0xff), sg);
  73. a[1] = hmul2(e2m1x2(p1 & 0xff), sg8);
  74. a[2] = hmul2(e2m1x2(p0 >> 8), sg);
  75. a[3] = hmul2(e2m1x2(p1 >> 8), sg8);
  76. }
  77. __device__ __forceinline__ void load_x(uint4 X[4], const __nv_bfloat16* x, int tok, int M, int K, int k) {
  78. if (tok >= M) {
  79. #pragma unroll
  80. for (int i = 0; i < 4; ++i) X[i] = make_uint4(0, 0, 0, 0);
  81. return;
  82. }
  83. const uint4* p = reinterpret_cast<const uint4*>(x + (size_t)tok * K + k);
  84. #pragma unroll
  85. for (int i = 0; i < 4; ++i) X[i] = bf16x8_to_f16x8(__ldg(p + i));
  86. }
  87. // One block: TILES 16-row tiles, KSPLIT warps per tile splitting K. Each warp
  88. // keeps the next chunk's weights in flight during this chunk's math.
  89. template <int TILES, int KSPLIT, int MT>
  90. __global__ void __launch_bounds__(TILES * KSPLIT * 32) dense_w4(
  91. const uint8_t* __restrict__ w, const uint32_t* __restrict__ s, const float* __restrict__ gs,
  92. const __nv_bfloat16* __restrict__ x, __nv_bfloat16* __restrict__ y, int M, int K, int N) {
  93. const int warp = threadIdx.x >> 5, lane = threadIdx.x & 31, g = lane >> 2, t = lane & 3;
  94. const int tl = warp % TILES, ks = warp / TILES;
  95. const int tile = blockIdx.x * TILES + tl;
  96. const bool live = tile * 16 < N;
  97. const int nchunk = K / 128;
  98. const uint8_t* w0 = w + (size_t)tile * nchunk * 1024 + 16 * lane;
  99. const uint32_t* sp = s + (size_t)tile * nchunk * 32 + lane;
  100. float c[MT][4] = {};
  101. const int c0 = nchunk * ks / KSPLIT, c1 = live ? nchunk * (ks + 1) / KSPLIT : c0;
  102. uint4 A0{}, A8{};
  103. uint32_t S = 0;
  104. if (c0 < c1) { A0 = ld_stream(w0 + (size_t)c0 * 1024); A8 = ld_stream(w0 + (size_t)c0 * 1024 + 512); S = __ldg(sp + c0 * 32); }
  105. for (int cc = c0; cc < c1; ++cc) {
  106. if constexpr (MT <= 4) {
  107. uint4 X[MT][4];
  108. #pragma unroll
  109. for (int mt = 0; mt < MT; ++mt) load_x(X[mt], x, mt * 8 + g, M, K, cc * 128 + 32 * t);
  110. const uint4 cA0 = A0, cA8 = A8;
  111. const uint32_t cS = S;
  112. if (cc + 1 < c1) {
  113. A0 = ld_stream(w0 + (size_t)(cc + 1) * 1024); A8 = ld_stream(w0 + (size_t)(cc + 1) * 1024 + 512);
  114. S = __ldg(sp + (cc + 1) * 32);
  115. }
  116. #pragma unroll
  117. for (int j = 0; j < 8; ++j) {
  118. uint32_t a[4];
  119. a_frag(a, cA0, cA8, cS, j);
  120. #pragma unroll
  121. for (int mt = 0; mt < MT; ++mt) {
  122. const uint4& xv = X[mt][j >> 1];
  123. const int wi = (j & 1) * 2;
  124. mma16816(c[mt], a, word(xv, wi), word(xv, wi + 1));
  125. }
  126. }
  127. } else {
  128. // Past 4 tiles, all of them holding x for the whole chunk runs out of
  129. // registers. Decode the chunk's weights once, then run the rows in two
  130. // halves of up to 4 tiles each, loading x one half at a time.
  131. const uint4 cA0 = A0, cA8 = A8;
  132. const uint32_t cS = S;
  133. if (cc + 1 < c1) {
  134. A0 = ld_stream(w0 + (size_t)(cc + 1) * 1024); A8 = ld_stream(w0 + (size_t)(cc + 1) * 1024 + 512);
  135. S = __ldg(sp + (cc + 1) * 32);
  136. }
  137. uint32_t a[8][4];
  138. #pragma unroll
  139. for (int j = 0; j < 8; ++j) a_frag(a[j], cA0, cA8, cS, j);
  140. #pragma unroll
  141. for (int h = 0; h < 2; ++h) {
  142. uint4 X[4][4];
  143. #pragma unroll
  144. for (int i = 0; i < 4; ++i)
  145. if (h * 4 + i < MT) load_x(X[i], x, (h * 4 + i) * 8 + g, M, K, cc * 128 + 32 * t);
  146. #pragma unroll
  147. for (int j = 0; j < 8; ++j)
  148. #pragma unroll
  149. for (int i = 0; i < 4; ++i)
  150. if (h * 4 + i < MT)
  151. mma16816(c[h * 4 + i], a[j], word(X[i][j >> 1], (j & 1) * 2), word(X[i][j >> 1], (j & 1) * 2 + 1));
  152. }
  153. }
  154. }
  155. if constexpr (KSPLIT > 1) {
  156. __shared__ float red[KSPLIT - 1][TILES][32][MT * 4];
  157. if (ks > 0)
  158. #pragma unroll
  159. for (int mt = 0; mt < MT; ++mt)
  160. #pragma unroll
  161. for (int i = 0; i < 4; ++i) red[ks - 1][tl][lane][mt * 4 + i] = c[mt][i];
  162. __syncthreads();
  163. if (ks > 0 || !live) return;
  164. #pragma unroll
  165. for (int r = 0; r < KSPLIT - 1; ++r)
  166. #pragma unroll
  167. for (int mt = 0; mt < MT; ++mt)
  168. #pragma unroll
  169. for (int i = 0; i < 4; ++i) c[mt][i] += red[r][tl][lane][mt * 4 + i];
  170. } else if (!live) {
  171. return;
  172. }
  173. #pragma unroll
  174. for (int i = 0; i < 4; ++i) {
  175. const int n = tile * 16 + g + (i >> 1) * 8;
  176. #pragma unroll
  177. for (int mt = 0; mt < MT; ++mt) {
  178. const int m = mt * 8 + 2 * t + (i & 1);
  179. if (m < M) y[(size_t)m * N + n] = __float2bfloat16(c[mt][i] * *gs);
  180. }
  181. }
  182. }
  183. // Tiled NVFP4 -> row-major bf16 [N][K], for batches too large for dense_w4.
  184. __global__ void dequant_w4(const uint8_t* __restrict__ w, const uint32_t* __restrict__ s, const float* __restrict__ gs,
  185. __nv_bfloat16* __restrict__ out, int K, int N) {
  186. const int nchunk = K / 128;
  187. const int tile = blockIdx.x, cc = blockIdx.y, lane = threadIdx.x & 31, half = threadIdx.x >> 5;
  188. const int g = lane >> 2, t = lane & 3, row = tile * 16 + half * 8 + g;
  189. const float gscale = *gs;
  190. const uint4 v = *reinterpret_cast<const uint4*>(w + ((size_t)tile * nchunk + cc) * 1024 + half * 512 + 16 * lane);
  191. const uint32_t sc = s[((size_t)tile * nchunk + cc) * 32 + lane];
  192. const uint32_t two = half ? (sc >> 16) : (sc & 0xffff);
  193. const float f0 = __half2float(__ushort_as_half((unsigned short)(e4m3x2(two) & 0xffff))) * gscale;
  194. const float f1 = __half2float(__ushort_as_half((unsigned short)(e4m3x2(two) >> 16))) * gscale;
  195. __nv_bfloat16* o = out + (size_t)row * K + cc * 128 + 32 * t;
  196. #pragma unroll
  197. for (int b = 0; b < 16; ++b) { // 16 bytes = 32 k; blocks of 16 k: first 8 bytes block 2t, rest 2t+1
  198. const uint32_t byte = (word(v, b >> 2) >> ((b & 3) * 8)) & 0xff;
  199. const uint32_t hv = e2m1x2(byte);
  200. const __half2 h = *reinterpret_cast<const __half2*>(&hv);
  201. const float f = b < 8 ? f0 : f1;
  202. o[2 * b] = __float2bfloat16(__low2float(h) * f);
  203. o[2 * b + 1] = __float2bfloat16(__high2float(h) * f);
  204. }
  205. }
  206. } // namespace
  207. // x [M, K] bf16, w/s tiled (see above), gscale [1] fp32 on the device, y [M, N] bf16. variant: tens digit
  208. // KSPLIT choice, units digit TILES choice; 0 picks from K.
  209. void megadense4(torch::Tensor x, torch::Tensor w, torch::Tensor s, torch::Tensor gscale, torch::Tensor y,
  210. int64_t variant) {
  211. const int M = x.size(0), K = x.size(1), N = y.size(1);
  212. TORCH_CHECK(M >= 1 && M <= 64 && K % 128 == 0 && N % 16 == 0 && x.is_contiguous() && y.is_contiguous());
  213. TORCH_CHECK(w.numel() == (int64_t)N * K / 2 && s.numel() * 4 == (int64_t)N * K / 16);
  214. auto stream = at::cuda::getCurrentCUDAStream();
  215. auto launch = [&](auto tiles, auto ksplit, auto mt) {
  216. constexpr int T = decltype(tiles)::value, KS = decltype(ksplit)::value, MT = decltype(mt)::value;
  217. dense_w4<T, KS, MT><<<(N / 16 + T - 1) / T, T * KS * 32, 0, stream>>>(
  218. reinterpret_cast<const uint8_t*>(w.data_ptr()), reinterpret_cast<const uint32_t*>(s.data_ptr()),
  219. gscale.data_ptr<float>(), reinterpret_cast<const __nv_bfloat16*>(x.data_ptr()),
  220. reinterpret_cast<__nv_bfloat16*>(y.data_ptr()), M, K, N);
  221. };
  222. using std::integral_constant;
  223. auto go = [&](auto mt) {
  224. int v = variant ? variant : (K >= 4096 ? 81 : K >= 2048 ? 41 : 21);
  225. switch (v) {
  226. case 81: launch(integral_constant<int, 1>{}, integral_constant<int, 8>{}, mt); break;
  227. case 82: launch(integral_constant<int, 2>{}, integral_constant<int, 8>{}, mt); break;
  228. case 41: launch(integral_constant<int, 1>{}, integral_constant<int, 4>{}, mt); break;
  229. case 42: launch(integral_constant<int, 2>{}, integral_constant<int, 4>{}, mt); break;
  230. case 44: launch(integral_constant<int, 4>{}, integral_constant<int, 4>{}, mt); break;
  231. case 21: launch(integral_constant<int, 1>{}, integral_constant<int, 2>{}, mt); break;
  232. case 24: launch(integral_constant<int, 4>{}, integral_constant<int, 2>{}, mt); break;
  233. default: launch(integral_constant<int, 4>{}, integral_constant<int, 1>{}, mt); break;
  234. }
  235. };
  236. if (M <= 8) go(integral_constant<int, 1>{});
  237. else if (M <= 16) go(integral_constant<int, 2>{});
  238. else if (M <= 24) go(integral_constant<int, 3>{});
  239. else if (M <= 32) go(integral_constant<int, 4>{});
  240. else if (M <= 40) go(integral_constant<int, 5>{});
  241. else if (M <= 48) go(integral_constant<int, 6>{});
  242. else if (M <= 56) go(integral_constant<int, 7>{});
  243. else go(integral_constant<int, 8>{});
  244. }
  245. void dequant4(torch::Tensor w, torch::Tensor s, torch::Tensor gscale, torch::Tensor out) {
  246. const int N = out.size(0), K = out.size(1);
  247. dequant_w4<<<dim3(N / 16, K / 128), 64, 0, at::cuda::getCurrentCUDAStream()>>>(
  248. reinterpret_cast<const uint8_t*>(w.data_ptr()), reinterpret_cast<const uint32_t*>(s.data_ptr()),
  249. gscale.data_ptr<float>(), reinterpret_cast<__nv_bfloat16*>(out.data_ptr()), K, N);
  250. }
  251. PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
  252. m.def("gemm", &megadense4);
  253. m.def("dequant", &dequant4);
  254. }