Files
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

396 lines
13 KiB
C++

// MIT License
//
// Copyright (c) 2026 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <hip/amd_detail/amd_hip_bf16.h>
#include <hip/amd_detail/amd_hip_fp8.h>
#include <hip/hip_runtime.h>
#include <chrono>
#include <cstring>
#include <iostream>
#ifdef ENABLE_ROCTX
# include <rocprofiler-sdk-roctx/roctx.h>
#else
# define roctxProfilerResume(_x)
# define roctxProfilerPause(_x)
#endif
#include "markers.hpp"
#define HIP_API_CALL(CALL) \
{ \
hipError_t error_ = (CALL); \
if (error_ != hipSuccess) \
{ \
fprintf(stderr, "%s:%d :: HIP error : %s\n", __FILE__, __LINE__, hipGetErrorString(error_)); \
throw std::runtime_error("hip_api_call"); \
} \
}
template <typename Type> class Matrix
{
public:
Matrix(int _rows, int _columns) : rows(_rows), columns(_columns), memsize(_rows * _columns * sizeof(Type))
{
host = new Type[rows * columns];
memset(host, 0, memsize);
HIP_API_CALL(hipMalloc((void**) &dev, memsize));
}
~Matrix()
{
if (hipDeviceSynchronize() != hipSuccess) abort();
if (hipFree(dev) != hipSuccess) abort();
delete[] host;
}
void toDevice()
{
HIP_API_CALL(hipMemcpy(dev, host, memsize, hipMemcpyDefault));
HIP_API_CALL(hipDeviceSynchronize());
}
void toHost()
{
HIP_API_CALL(hipMemcpy(host, dev, memsize, hipMemcpyDefault));
HIP_API_CALL(hipDeviceSynchronize());
}
const int rows;
const int columns;
const int memsize;
Type* host;
Type* dev;
};
#define SHMBLOCK 64
#define TBLOCK 16
using float16 = __hip_bfloat16;
using float8 = __hip_fp8_e4m3_fnuz;
using Vec4 = __attribute__((__vector_size__(4 * sizeof(float)))) float;
using IVec4 = __attribute__((__vector_size__(4 * sizeof(uint32_t)))) uint32_t;
using Vec16 = __attribute__((__vector_size__(16 * sizeof(float)))) float;
using Vec8 = __attribute__((__vector_size__(8 * sizeof(float)))) float;
using float8x8 = __attribute__((__vector_size__(8 * sizeof(uint8_t)))) uint8_t;
using IVec2 = __attribute__((__vector_size__(2 * sizeof(int)))) int;
using IVec8 = __attribute__((__vector_size__(8 * sizeof(int)))) int;
static_assert(sizeof(float8) == sizeof(uint8_t));
#if defined(__gfx942__) || defined(__gfx950__)
inline __device__ uint64_t castfrom8x8(const float8x8& f) { return *reinterpret_cast<const uint64_t*>(&f); }
inline __device__ Vec4 mfma(const float8x8& a, const float8x8& b, const Vec4& initial = Vec4{})
{
return __builtin_amdgcn_mfma_f32_16x16x32_fp8_fp8(castfrom8x8(a), castfrom8x8(b), initial, 0, 0, 0);
}
inline __device__ Vec4 mfma(const float8x8& a0, const float8x8& a1, const float8x8& b0, const float8x8& b1)
{
Vec4 ret = mfma(a0, b0);
return mfma(a1, b1, ret);
}
#else
# if defined(__gfx1200__) || defined(__gfx1201__)
inline __device__ IVec2 castfrom8x8(const float8x8& f) { return *reinterpret_cast<const IVec2*>(&f); }
inline __device__ Vec8 mfma(const float8x8& a, const float8x8& b, const Vec8& initial = Vec8{})
{
return __builtin_amdgcn_wmma_f32_16x16x16_fp8_fp8_w32_gfx12(castfrom8x8(a), castfrom8x8(b), initial);
}
# else
inline __device__ IVec8 castfrom8x8(const float8x8& f) { return *reinterpret_cast<const IVec8*>(&f); }
inline __device__ Vec8 mfma(const IVec8& a, const IVec8& b, const Vec8& initial = Vec8{})
{
return __builtin_amdgcn_wmma_f32_16x16x64_fp8_fp8(a, b, 0, initial, 0, 0);
}
# endif
inline __device__ Vec4 mfma(const float8x8& a0, const float8x8& a1, const float8x8& b0, const float8x8& b1)
{
Vec8 ret = mfma(castfrom8x8(a0), castfrom8x8(b0));
ret = mfma(castfrom8x8(a1), castfrom8x8(b1), ret);
return {ret[0], ret[1], ret[2], ret[3]};
}
#endif
// HEIGHT <= 4
// WIDTH > 4 causes register spill
template <int WIDTH, int HEIGHT = 4> __global__ void __launch_bounds__(TBLOCK* TBLOCK * 2, 2) fp8_gemm_kernel(
const uint8_t* __restrict__ a,
const uint8_t* __restrict__ b,
float16* __restrict__ c,
const float* __restrict__ scale_a,
const float* __restrict__ scale_b,
int MDIM,
int NDIM,
int KDIM
)
{
const int X = blockIdx.x * SHMBLOCK * WIDTH;
const int Y = blockIdx.y * TBLOCK * HEIGHT;
const int TX = threadIdx.x % TBLOCK;
const int TY = threadIdx.y % 4;
const int TZ = (threadIdx.y / 4) % 4;
const int TYZ = threadIdx.y % 16;
const int TW = (threadIdx.y / 16) % 2;
__shared__ uint8_t a_shared[WIDTH][4][4][4][TBLOCK][4];
__shared__ uint8_t b_shared[4][HEIGHT][4][TBLOCK][4];
__shared__ float scalar[4][TBLOCK][WIDTH];
if (TW != 0)
{
sqtt_marker_enter("Producer Thread");
for (int k1 = 0; k1 < KDIM; k1 += 2 * SHMBLOCK)
for (int k2 = 0; k2 < 2 * SHMBLOCK; k2 += SHMBLOCK)
{
sqtt_marker_enter("VRAM matrix load");
int k0 = k1 + k2;
IVec4 temp_a[WIDTH], temp_b;
int tx3 = (TX & 3) << 2;
int i1 = TX >> 2;
#pragma unroll
for (int j1 = 0; j1 < 4; j1++)
if (k0 + j1 * TBLOCK < KDIM && Y + 4 * TX < NDIM)
temp_b[j1] =
*reinterpret_cast<const uint32_t*>(&b[(k0 + TZ * TBLOCK + TY + 4 * j1) * NDIM + Y + 4 * TX]
);
#pragma unroll
for (int j1 = 0; j1 < 4; j1++)
if (k0 + j1 * TBLOCK < KDIM && X < MDIM)
#pragma unroll
for (int n = 0; n < WIDTH; n++)
temp_a[n][j1] = (X + n * SHMBLOCK < MDIM)
? *reinterpret_cast<const uint32_t*>(
&a[(k0 + j1 * TBLOCK + TYZ) * MDIM + X + 4 * TX + n * SHMBLOCK]
)
: 0;
sqtt_marker_exit("VRAM matrix load");
if (k0 != 0) __syncthreads();
sqtt_marker_enter("Store matrix to DS");
if (i1 < HEIGHT)
#pragma unroll
for (int j1 = 0; j1 < 4; j1++)
#pragma unroll
for (int m = 0; m < 4; m++) b_shared[TZ][i1][TY][tx3 + m][j1] = (temp_b[j1] >> (8 * m)) & 0xFF;
#pragma unroll
for (int j1 = 0; j1 < 4; j1++)
#pragma unroll
for (int m = 0; m < 4; m++)
#pragma unroll
for (int n = 0; n < WIDTH; n++)
a_shared[n][i1][TZ][TY][tx3 + m][j1] = (temp_a[n][j1] >> (8 * m)) & 0xFF;
sqtt_marker_exit("Store matrix to DS");
__syncthreads();
}
sqtt_marker_exit("Producer Thread");
return;
}
sqtt_marker_enter("Consumer Thread");
Vec16 reg_res[WIDTH];
#pragma unroll
for (int n = 0; n < WIDTH; n++) reg_res[n] = {};
float tmp_a[WIDTH];
float tmp_b;
if (TZ == 0)
{
sqtt_marker_enter("Preload scale");
#pragma unroll
for (int n = 0; n < WIDTH; n++)
tmp_a[n] = (X + TY * TBLOCK + n * SHMBLOCK < MDIM) ? scale_a[X + TY * TBLOCK + TX + n * SHMBLOCK] : 0;
tmp_b = scale_b[Y / 128];
sqtt_marker_exit("Preload scale");
}
for (int k1 = 0; k1 < KDIM; k1 += 2 * SHMBLOCK)
{
if (TZ == 0)
{
sqtt_marker_enter("Preload scale");
for (int n = 0; n < WIDTH; n++) scalar[TY][TX][n] = tmp_b * tmp_a[n];
if (k1 + 2 * SHMBLOCK < KDIM)
{
#pragma unroll
for (int n = 0; n < WIDTH; n++)
tmp_a[n] = (X + TY * TBLOCK + n * SHMBLOCK < MDIM)
? scale_a[(k1 / 128 + 1) * MDIM + X + TY * TBLOCK + TX + n * SHMBLOCK]
: 0;
tmp_b = scale_b[(k1 / 128 + 1) * ((NDIM + 127) / 128) + (Y / 128)];
}
sqtt_marker_exit("Preload scale");
}
for (int k2 = 0; k2 < 2 * SHMBLOCK; k2 += SHMBLOCK)
{
int k0 = k1 + k2;
sqtt_marker_enter("Wait for producer");
__syncthreads();
sqtt_marker_exit("Wait for producer");
sqtt_marker_enter("Load matrix from DS");
float8x8 a0_load[WIDTH], a1_load[WIDTH];
float8x8 b0_load[HEIGHT], b1_load[HEIGHT];
#pragma unroll
for (int m = 0; m < 8; m++)
#pragma unroll
for (int n = 0; n < HEIGHT; n++)
{
b0_load[n][m] = b_shared[m / 4 + 0][n][TY][TX][m % 4];
b1_load[n][m] = b_shared[m / 4 + 2][n][TY][TX][m % 4];
}
#pragma unroll
for (int m = 0; m < 8; m++)
#pragma unroll
for (int r = 0; r < WIDTH; r++)
{
a0_load[r][m] = a_shared[r][TZ][m % 4][TY][TX][m / 4 + 0];
a1_load[r][m] = a_shared[r][TZ][m % 4][TY][TX][m / 4 + 2];
}
Vec4 scal[WIDTH];
#pragma unroll
for (int n = 0; n < WIDTH; n++)
#pragma unroll
for (int m = 0; m < 4; m++) scal[n][m] = scalar[TZ][TY * 4 + m][n];
sqtt_marker_exit("Load matrix from DS");
sqtt_marker_enter("Wait for producer");
__syncthreads();
sqtt_marker_exit("Wait for producer");
sqtt_marker_enter("MFMA Section");
#pragma unroll
for (int n = 0; n < HEIGHT; n++)
#pragma unroll
for (int r1 = 0; r1 < WIDTH; r1++)
{
Vec4 res = mfma(a0_load[r1], a1_load[r1], b0_load[n], b1_load[n]);
for (int m = 0; m < 4; m++) reg_res[r1][m * HEIGHT + n] += scal[r1][m] * res[m];
}
sqtt_marker_exit("MFMA Section");
}
}
for (int j = 0; j < 4; j++)
for (int i = 0; i < HEIGHT; i++)
for (int r1 = 0; r1 < WIDTH; r1++)
if (Y + i * TBLOCK < NDIM && 4 * TYZ + X + r1 * SHMBLOCK < MDIM)
c[(4 * TYZ + j + X + r1 * SHMBLOCK) * NDIM + Y + i * TBLOCK + TX] =
(float16) reg_res[r1][j * HEIGHT + i];
sqtt_marker_exit("Consumer Thread");
}
void launchHip(const void* a, const void* b, void* c, const float* scale_a, const float* scale_b, int M, int N, int K)
{
dim3 block(TBLOCK, 32, 1);
dim3 grid((M + 4 * SHMBLOCK - 1) / SHMBLOCK / 4, N / SHMBLOCK);
fp8_gemm_kernel<4><<<grid, block, 0, 0>>>(
reinterpret_cast<const uint8_t*>(a),
reinterpret_cast<const uint8_t*>(b),
reinterpret_cast<float16*>(c),
scale_a,
scale_b,
M,
N,
K
);
HIP_API_CALL(hipGetLastError());
HIP_API_CALL(hipDeviceSynchronize());
}
int main()
{
const int device = 0;
int M = 6144;
int K = 7168;
int N = 4608;
hipDeviceProp_t devProp{};
HIP_API_CALL(hipGetDeviceProperties(&devProp, device));
HIP_API_CALL(hipSetDevice(device));
if (devProp.major > 9 && devProp.minor == 0)
{
M /= 3;
K /= 3;
N /= 3;
}
else if (devProp.major > 9 && devProp.minor == 5)
{
M /= 2;
K /= 2;
}
Matrix<float8> a(K, M);
Matrix<float8> b(K, N);
Matrix<float16> c(M, N);
Matrix<float> scale_a(K / 128, M);
Matrix<float> scale_b(K / 128, N / 128);
// warmup
launchHip(a.dev, b.dev, c.dev, scale_a.dev, scale_b.dev, M, N, K);
HIP_API_CALL(hipDeviceSynchronize());
roctxProfilerResume(0);
int runs = 10;
for (int i = 0; i < runs; i++) launchHip(a.dev, b.dev, c.dev, scale_a.dev, scale_b.dev, M, N, K);
HIP_API_CALL(hipDeviceSynchronize());
roctxProfilerPause(0);
return 0;
}