| #include <hip/hip_runtime.h> |
| #include <rocwmma/rocwmma.hpp> |
| #include <cmath> |
| #include <cstdio> |
| #include <cstdlib> |
| #include <cstring> |
|
|
| using half_t = _Float16; |
|
|
| template <int BlockThreads> |
| __global__ void gemm16x16_mfma( |
| const half_t* __restrict__ A, |
| const half_t* __restrict__ B, |
| const float* __restrict__ C, |
| float* __restrict__ D, |
| int M, |
| int N, |
| int K) |
| { |
| using namespace rocwmma; |
|
|
| constexpr int WM = 16; |
| constexpr int WN = 16; |
| constexpr int WK = 16; |
| constexpr int warpSize = hipWarpSize; |
| constexpr int WavesPerBlock = BlockThreads / warpSize; |
|
|
| static_assert(BlockThreads % warpSize == 0); |
|
|
| const int tid = threadIdx.x; |
| const int wave = tid / warpSize; |
|
|
| const int tileM = (blockIdx.y * WavesPerBlock + wave) * WM; |
| const int tileN = blockIdx.x * WN; |
|
|
| extern __shared__ unsigned char smemRaw[]; |
|
|
| |
| auto* ldsA = reinterpret_cast<half_t*>(smemRaw); |
| auto* ldsB = ldsA + WavesPerBlock * WM * WK; |
|
|
| |
| half_t* waveA = ldsA + wave * WM * WK; |
| half_t* waveB = ldsB + wave * WK * WN; |
|
|
| |
| fragment<accumulator, WM, WN, WK, float> acc; |
| fill_fragment(acc, 0.0f); |
|
|
| |
| if (tileM < M && tileN < N) { |
| |
| |
| load_matrix_sync(acc, C + tileM * N + tileN, N, mem_row_major); |
| } |
| |
|
|
| |
| for (int kBase = 0; kBase < K; kBase += WK) { |
| |
| for (int idx = tid; idx < WavesPerBlock * WM * WK; idx += BlockThreads) { |
| const int ownerWave = idx / (WM * WK); |
| const int local = idx % (WM * WK); |
| const int row = local / WK; |
| const int col = local % WK; |
|
|
| const int globalM = (blockIdx.y * WavesPerBlock + ownerWave) * WM + row; |
| const int globalK = kBase + col; |
|
|
| |
| half_t val = half_t(0); |
| if (globalM < M && globalK < K) { |
| val = A[globalM * K + globalK]; |
| } |
| ldsA[idx] = val; |
| } |
|
|
| |
| for (int idx = tid; idx < WavesPerBlock * WK * WN; idx += BlockThreads) { |
| const int ownerWave = idx / (WK * WN); |
| const int local = idx % (WK * WN); |
| const int row = local / WN; |
| const int col = local % WN; |
|
|
| const int globalK = kBase + row; |
| const int globalN = blockIdx.x * WN + col; |
|
|
| half_t val = half_t(0); |
| if (globalK < K && globalN < N) { |
| val = B[globalK * N + globalN]; |
| } |
| ldsB[idx] = val; |
| } |
|
|
| |
| __syncthreads(); |
|
|
| |
| fragment<matrix_a, WM, WN, WK, half_t, row_major> a; |
| fragment<matrix_b, WM, WN, WK, half_t, col_major> b; |
|
|
| |
| load_matrix_sync(a, waveA, WK); |
| load_matrix_sync(b, waveB, WN); |
|
|
| |
| mfma_sync(acc, a, b, acc); |
|
|
| |
| __syncthreads(); |
| } |
|
|
| |
| if (tileM < M && tileN < N) { |
| store_matrix_sync(D + tileM * N + tileN, acc, N, mem_row_major); |
| } |
| |
| } |
|
|
| |
| void run_test(int test_case) { |
| const int M = 16, N = 16, K = 16; |
| const size_t A_size = M * K; |
| const size_t B_size = K * N; |
| const size_t C_size = M * N; |
| const size_t D_size = M * N; |
|
|
| half_t *h_A = (half_t*)malloc(A_size * sizeof(half_t)); |
| half_t *h_B = (half_t*)malloc(B_size * sizeof(half_t)); |
| float *h_C = (float*)malloc(C_size * sizeof(float)); |
| float *h_D = (float*)malloc(D_size * sizeof(float)); |
| float *h_D_ref = (float*)malloc(D_size * sizeof(float)); |
|
|
| |
| memset(h_A, 0, A_size * sizeof(half_t)); |
| memset(h_B, 0, B_size * sizeof(half_t)); |
| memset(h_C, 0, C_size * sizeof(float)); |
|
|
| |
| half_t inf = __float2half(INFINITY); |
| half_t neg_inf = __float2half(-INFINITY); |
| half_t nan = __float2half(NAN); |
| float nanf = NAN; |
|
|
| switch (test_case) { |
| case 0: |
| for (size_t i = 0; i < A_size; i++) h_A[i] = __float2half(1.0f); |
| for (size_t i = 0; i < B_size; i++) h_B[i] = __float2half(1.0f); |
| break; |
| case 1: |
| h_A[0] = nan; |
| break; |
| case 2: |
| h_B[0] = nan; |
| break; |
| case 3: |
| h_C[0] = nanf; |
| break; |
| case 4: |
| |
| h_B[0] = inf; |
| break; |
| case 5: |
| h_A[0] = inf; |
| break; |
| case 6: |
| h_A[0] = __float2half(1.0f); |
| h_B[0] = inf; |
| h_A[1] = __float2half(1.0f); |
| h_B[16] = neg_inf; |
| break; |
| default: |
| printf("Invalid test case %d\n", test_case); |
| free(h_A); free(h_B); free(h_C); free(h_D); free(h_D_ref); |
| return; |
| } |
|
|
| |
| half_t *d_A, *d_B; |
| float *d_C, *d_D; |
| hipMalloc(&d_A, A_size * sizeof(half_t)); |
| hipMalloc(&d_B, B_size * sizeof(half_t)); |
| hipMalloc(&d_C, C_size * sizeof(float)); |
| hipMalloc(&d_D, D_size * sizeof(float)); |
| hipMemcpy(d_A, h_A, A_size * sizeof(half_t), hipMemcpyHostToDevice); |
| hipMemcpy(d_B, h_B, B_size * sizeof(half_t), hipMemcpyHostToDevice); |
| hipMemcpy(d_C, h_C, C_size * sizeof(float), hipMemcpyHostToDevice); |
| hipMemset(d_D, 0, D_size * sizeof(float)); |
|
|
| |
| constexpr int BlockThreads = 256; |
| const int warpSize = hipWarpSize; |
| const int WavesPerBlock = BlockThreads / warpSize; |
| dim3 block(BlockThreads); |
| dim3 grid( |
| (N + 15) / 16, |
| (M + 16 * WavesPerBlock - 1) / (16 * WavesPerBlock) |
| ); |
|
|
| gemm16x16_mfma<BlockThreads><<<grid, block>>>(d_A, d_B, d_C, d_D, M, N, K); |
| hipDeviceSynchronize(); |
|
|
| |
| hipMemcpy(h_D, d_D, D_size * sizeof(float), hipMemcpyDeviceToHost); |
|
|
| |
| for (int m = 0; m < M; m++) { |
| for (int n = 0; n < N; n++) { |
| float acc = h_C[m * N + n]; |
| for (int k = 0; k < K; k++) { |
| half_t a = h_A[m * K + k]; |
| half_t b = h_B[k * N + n]; |
| float product = __half2float(__hmul(a, b)); |
| acc += product; |
| } |
| h_D_ref[m * N + n] = acc; |
| } |
| } |
|
|
| |
| bool passed = true; |
| for (size_t i = 0; i < D_size; i++) { |
| float ref = h_D_ref[i]; |
| float res = h_D[i]; |
| if (std::isnan(ref)) { |
| if (!std::isnan(res)) { |
| printf("Error at %zu: expected NaN, got %f\n", i, res); |
| passed = false; |
| } |
| } else { |
| if (std::isnan(res)) { |
| printf("Error at %zu: expected %f, got NaN\n", i, ref); |
| passed = false; |
| } else { |
| float diff = fabsf(ref - res); |
| if (diff > 1e-5f) { |
| printf("Error at %zu: expected %f, got %f (diff=%f)\n", i, ref, res, diff); |
| passed = false; |
| } |
| } |
| } |
| } |
|
|
| if (passed) { |
| printf("Test case %d passed.\n", test_case); |
| } else { |
| printf("Test case %d failed.\n", test_case); |
| } |
|
|
| |
| free(h_A); free(h_B); free(h_C); free(h_D); free(h_D_ref); |
| hipFree(d_A); hipFree(d_B); hipFree(d_C); hipFree(d_D); |
| } |
|
|
| int main() { |
| |
| for (int test_case = 0; test_case <= 6; test_case++) { |
| run_test(test_case); |
| } |
| return 0; |
| } |