1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
| template <typename Config> __global__ void multistage_gemm_kernel(half_t* Dptr, const half_t* Aptr, const half_t* Bptr, int M, int N, int K) { using T = half_t; using SmemLayoutA = typename Config::SmemLayoutA; using SmemLayoutB = typename Config::SmemLayoutB; using TiledMMA = typename Config::MMA; using S2RCopyAtomA = typename Config::S2RCopyAtomA; using S2RCopyAtomB = typename Config::S2RCopyAtomB; using G2SCopyA = typename Config::G2SCopyA; using G2SCopyB = typename Config::G2SCopyB;
constexpr int kTileM = Config::kTileM; constexpr int kTileN = Config::kTileN; constexpr int kTileK = Config::kTileK; constexpr int kStage = Config::kStage;
extern __shared__ T shm_data[]; T* Ashm = shm_data; T* Bshm = shm_data + cosize(SmemLayoutA{});
int idx = threadIdx.x; int bx = blockIdx.x; int by = blockIdx.y;
auto A = make_tensor(make_gmem_ptr(Aptr), make_shape(M, K), make_stride(K, Int<1>{})); auto B = make_tensor(make_gmem_ptr(Bptr), make_shape(N, K), make_stride(K, Int<1>{})); auto D = make_tensor(make_gmem_ptr(Dptr), make_shape(M, N), make_stride(N, Int<1>{}));
auto gA = local_tile(A, make_tile(Int<kTileM>{}, Int<kTileK>{}), make_coord(by, _)); auto gB = local_tile(B, make_tile(Int<kTileN>{}, Int<kTileK>{}), make_coord(bx, _)); auto gD = local_tile(D, make_tile(Int<kTileM>{}, Int<kTileN>{}), make_coord(by, bx));
auto sA = make_tensor(make_smem_ptr(Ashm), SmemLayoutA{}); auto sB = make_tensor(make_smem_ptr(Bshm), SmemLayoutB{});
TiledMMA tiled_mma; auto thr_mma = tiled_mma.get_slice(idx); auto tCrA = thr_mma.partition_fragment_A(gA(_, _, 0)); auto tCrB = thr_mma.partition_fragment_B(gB(_, _, 0)); auto tCrD = thr_mma.partition_fragment_C(gD); clear(tCrD);
auto s2r_tiled_copy_a = make_tiled_copy_A(S2RCopyAtomA{}, tiled_mma); auto s2r_thr_copy_a = s2r_tiled_copy_a.get_slice(idx); auto tAsA = s2r_thr_copy_a.partition_S(sA); auto tCrA_view = s2r_thr_copy_a.retile_D(tCrA);
auto s2r_tiled_copy_b = make_tiled_copy_B(S2RCopyAtomB{}, tiled_mma); auto s2r_thr_copy_b = s2r_tiled_copy_b.get_slice(idx); auto tBsB = s2r_thr_copy_b.partition_S(sB); auto tCrB_view = s2r_thr_copy_b.retile_D(tCrB);
G2SCopyA g2s_tiled_copy_a; auto g2s_thr_copy_a = g2s_tiled_copy_a.get_slice(idx); auto tAgA_copy = g2s_thr_copy_a.partition_S(gA); auto tAsA_copy = g2s_thr_copy_a.partition_D(sA);
G2SCopyB g2s_tiled_copy_b; auto g2s_thr_copy_b = g2s_tiled_copy_b.get_slice(idx); auto tBgB_copy = g2s_thr_copy_b.partition_S(gB); auto tBsB_copy = g2s_thr_copy_b.partition_D(sB);
int itile_to_read = 0; int ismem_read = 0; int ismem_write = 0;
#pragma unroll for (int istage = 0; istage < kStage - 1; ++istage) { cute::copy(g2s_tiled_copy_a, tAgA_copy(_, _, _, istage), tAsA_copy(_, _, _, istage)); cute::copy(g2s_tiled_copy_b, tBgB_copy(_, _, _, istage), tBsB_copy(_, _, _, istage)); cp_async_fence(); ++itile_to_read; ++ismem_write; }
cp_async_wait<kStage - 2>(); __syncthreads();
int ik = 0; cute::copy(s2r_tiled_copy_a, tAsA(_, _, ik, ismem_read), tCrA_view(_, _, ik)); cute::copy(s2r_tiled_copy_b, tBsB(_, _, ik, ismem_read), tCrB_view(_, _, ik));
int ntile = K / kTileK; #pragma unroll 1 for (int itile = 0; itile < ntile; ++itile) { int nk = size<2>(tCrA);
#pragma unroll for (int ik = 0; ik < nk; ++ik) { int ik_next = (ik + 1) % nk;
if (ik == nk - 1) { cp_async_wait<kStage - 2>(); __syncthreads(); ismem_read = (ismem_read + 1) % kStage; }
cute::copy(s2r_tiled_copy_a, tAsA(_, _, ik_next, ismem_read), tCrA_view(_, _, ik_next)); cute::copy(s2r_tiled_copy_b, tBsB(_, _, ik_next, ismem_read), tCrB_view(_, _, ik_next));
if (ik == 0) { if (itile_to_read < ntile) { cute::copy(g2s_tiled_copy_a, tAgA_copy(_, _, _, itile_to_read), tAsA_copy(_, _, _, ismem_write)); cute::copy(g2s_tiled_copy_b, tBgB_copy(_, _, _, itile_to_read), tBsB_copy(_, _, _, ismem_write)); ++itile_to_read; ismem_write = (ismem_write + 1) % kStage; } cp_async_fence(); }
cute::gemm(tiled_mma, tCrD, tCrA(_, _, ik), tCrB(_, _, ik), tCrD); } }
auto tDgD = thr_mma.partition_C(gD); cute::copy(tCrD, tDgD); }
struct MultiStageConfig { using T = half_t; static constexpr int kTileM = 128; static constexpr int kTileN = 128; static constexpr int kTileK = 32; static constexpr int kStage = 3;
using SmemLayoutAtom = decltype(composition( Swizzle<3, 3, 3>{}, make_layout(make_shape(Int<8>{}, Int<kTileK>{}), make_stride(Int<kTileK>{}, Int<1>{})))); using SmemLayoutA = decltype(tile_to_shape( SmemLayoutAtom{}, make_shape(Int<kTileM>{}, Int<kTileK>{}, Int<kStage>{}))); using SmemLayoutB = decltype(tile_to_shape( SmemLayoutAtom{}, make_shape(Int<kTileN>{}, Int<kTileK>{}, Int<kStage>{})));
using mma_op = SM80_16x8x16_F16F16F16F16_TN; using mma_atom = MMA_Atom<MMA_Traits<mma_op>>; using MMA = decltype(make_tiled_mma( mma_atom{}, make_layout(make_shape(Int<2>{}, Int<2>{}, Int<1>{})), Tile<Int<32>, Int<32>, Int<16>>{}));
using g2s_copy_op = SM80_CP_ASYNC_CACHEGLOBAL<cute::uint128_t>; using g2s_copy_atom = Copy_Atom<Copy_Traits<g2s_copy_op>, T>; using G2SCopyA = decltype(make_tiled_copy( g2s_copy_atom{}, make_layout(make_shape(Int<32>{}, Int<4>{}), make_stride(Int<4>{}, Int<1>{})), make_layout(make_shape(Int<1>{}, Int<8>{})))); using G2SCopyB = G2SCopyA;
using s2r_copy_atom = Copy_Atom<Copy_Traits<SM75_U32x4_LDSM_N>, T>; using S2RCopyAtomA = s2r_copy_atom; using S2RCopyAtomB = s2r_copy_atom;
static constexpr int kThreadNum = size(MMA{}); static constexpr int kShmSize = (cosize(SmemLayoutA{}) + cosize(SmemLayoutB{})) * sizeof(T); };
|