AMD 矩阵核心

Originally posted:
最后更新:
Gina Sitaraman's avatar
Gina Sitaraman
通讯作者
Noel Chalmers's avatar
Noel Chalmers
作者
Nicholas Malaya's avatar
Nicholas Malaya
作者
Damon McDougall's avatar
Damon McDougall
作者
Ossian O'Reilly's avatar
Ossian O'Reilly
作者
Rene Van Oostrum's avatar
Rene Van Oostrum
作者
Joseph Greathouse
审稿人

矩阵乘法是线性代数中的一个基本方面,并且是高性能计算(HPC)应用程序中无处不在的计算。自 AMD CDNA 架构推出以来,通用矩阵乘法 (GEMM) 计算现已通过矩阵核心处理单元得到硬件加速。矩阵核心加速的 GEMM 内核是 rocBLAS 等 BLAS 库的核心,但开发者也可以直接对其进行编程。受 GEMM 计算吞吐量限制的应用程序可以通过利用矩阵核心获得额外的加速。

AMD 的矩阵核心技术支持各种混合精度运算,使我们能够处理大型模型并为任何人工智能和机器学习工作负载组合提高内存密集型运算的性能。各种数值格式在不同应用中有其用途。例如,使用 8 位整数 (INT8) 进行 ML 推理,使用 32 位浮点数 (FP32) 数据进行 ML 训练和 HPC 应用,使用 16 位浮点数 (FP16) 数据进行图形工作负载,以及使用 16 位脑浮点数 (BF16) 数据进行 ML 训练,并减少收敛问题。

要详细了解与 SIMD 向量单元相比使用矩阵核心可实现的理论加速,请参阅下表。这些表列出了上一代 (MI100) 和当前一代 (MI250X) CDNA 加速器的向量(即融合乘加或 FMA)和矩阵核心单元的性能。

MI100 和 MI250X 的矩阵核心性能

数据格式MI100 Flops/Clock/CUMI250X Flops/Clock/CU
FP64不适用256
FP32256256
FP1610241024
BF165121024
INT810241024

MI100 和 MI250X 的向量 (FMA) 单元性能

数据格式MI100 Flops/Clock/CUMI250X Flops/Clock/CU
FP6464128
FP32128128

MI100 和 MI250X 的矩阵核心与向量单元性能的加速比。注意,MI250X 还支持打包 FP32 指令,这使 FP32 吞吐量加倍

数据格式MI100 矩阵/向量加速比MI250X 矩阵/向量加速比
FP64不适用2x
FP322x2x

使用 AMD 矩阵核心

AMD CDNA GPU 中的矩阵融合乘加 (MFMA) 指令是基于波前 (wavefront) 而非单个通道 (lane/thread) 的:输入和输出矩阵的元素分布在波前向量寄存器的通道上。

AMD 矩阵核心可以通过多种方式利用。从高层来看,可以使用 rocBLAS 或 rocWMMA 等库在 GPU 上执行矩阵运算。例如,如果 MFMA 指令对当前计算有利,rocBLAS 可能会选择使用它们。对于更底层的操作,您可以选择

  • 完全用汇编语言编写 GPU 内核(这可能有些挑战且不切实际)

  • 在 HIP 内核中嵌入内联汇编(不推荐,因为编译器不会考虑内联指令的语义,并且可能不会处理数据依赖,例如在使用 MFMA 指令的结果之前必须等待的指定周期数)

  • 使用编译器内在函数:这些函数代表汇编指令,以便编译器能够理解其语义和要求。

本文中的编码示例使用了一些可用的 MFMA 指令的编译器内在函数,并展示了如何将输入和输出矩阵的元素映射到波前向量寄存器的通道。所有示例都使用单个波前来计算小尺寸的矩阵乘法。这些示例并非旨在展示如何从 MFMA 操作中获得高吞吐量。

MFMA 编译器内在函数语法

考虑以下 MFMA 乘法运算,其中所有操作数 AABBCCDD 均为矩阵:D=AB+CD = A B + C

要在 AMD GPU 上执行 MFMA 操作,LLVM 提供了内置的内在函数。请注意,这些内在函数是在整个波前上执行的,并且输入和输出矩阵的片段被加载到波前每个通道的寄存器中。MFMA 编译器内在函数的语法如下所示。

d = __builtin_amdgcn_mfma_CDFmt_MxNxKABFmt (a, b, c, cbsz, abid, blgp)

其中,

  • CDfmtCCDD 矩阵的数据格式

  • ABfmtAABB 矩阵的数据格式

  • MNK 是矩阵的维度

    • mA[M][K]AA 矩阵

    • mB[K][N]BB 矩阵

    • mC[M][N] 累加输入矩阵 CC

    • mD[M][N] 累加结果矩阵 DD

  • a 是存储来自源矩阵 AA 的值的向量寄存器集合

  • b 是存储来自源矩阵 BB 的值的向量寄存器集合

  • c 是存储来自累加输入矩阵 CC 的值的向量寄存器集合

  • d 是存储累加结果矩阵 DD 的值的向量寄存器集合

  • cbsz(控制广播大小修饰符)用于更改输入值被馈送到矩阵核心的方式,并且仅支持具有多个输入块的 AA 矩阵的指令。设置 cbsz 会告知指令将一个选定输入块的值广播到 AA 中的 2cbsz2^{cbsz} 个相邻块。用于广播的输入块由 abid 参数确定。默认值 0 表示不进行值广播。例如,对于一个 16 块的 AA 矩阵,设置 cbsz=1 将导致块 0 和 1 接收相同的值,块 2 和 3 接收相同的值,块 4 和 5 接收相同的值,依此类推。

  • abid(A 矩阵广播标识符)支持具有多个输入块的 AA 矩阵的指令。它与 cbsz 一起使用,并指示选择哪个输入块以广播到 AA 矩阵中的其他相邻块。例如,对于一个 16 块的 AA 矩阵,设置 cbsz=2abid=1 将导致块 1 的值被广播到块 0-3,块 5 的值被广播到块 4-7,块 9 的值被广播到块 8-11,依此类推。

  • blgp(B 矩阵通道组模式修饰符)允许对 BB 矩阵数据在通道之间进行一组受限的混洗操作。对于支持此修饰符的指令,支持以下值:

    • blgp=0 BB 的正常矩阵布局

    • blgp=1 通道 0-31 的 BB 矩阵数据也广播到通道 32-63

    • blgp=2 通道 32-63 的 BB 矩阵数据广播到通道 0-31

    • blgp=3 BB 矩阵数据从所有通道向下旋转 16(例如,通道 0 的数据放入通道 48,通道 16 的数据放入通道 0)

    • blgp=4 通道 0-15 的 BB 矩阵数据广播到通道 16-31、32-47 和 48-63

    • blgp=5 通道 16-31 的 BB 矩阵数据广播到通道 0-15、32-47 和 48-63

    • blgp=6 通道 32-47 的 BB 矩阵数据广播到通道 0-15、16-31 和 48-63

    • blgp=7 通道 48-63 的 BB 矩阵数据广播到通道 0-15、16-31 和 32-47

CDNA2 GPU 支持的矩阵维度和块数如下表所示。

A/B 数据格式C/D 数据格式MNK周期Flops/周期/CU
FP32FP32
32322164256
32321264256
16164132256
16161432256
441168256
FP16FP32
323281641024
323242641024
1616161321024
161644321024
4441681024
INT8INT32
323281641024
323242641024
1616161321024
161644321024
4441681024
BF16FP32
323281641024
323242641024
1616161321024
161644321024
4441681024
32324164512
32322264512
16168132512
16162432512
442168512
FP64FP64
16164132256
444416128

所有 CDNA2 架构支持的指令的完整列表可以在 AMD Instinct MI200 指令集架构参考指南 中找到。AMD 的 矩阵指令计算器 工具可以生成更多信息,例如 AMD Radeon™ 和 AMD Instinct™ 加速器上 MFMA 指令的计算吞吐量和寄存器使用情况。

示例 1 – V_MFMA_F32_16x16x4F32

考虑矩阵乘法运算 D=ABD = A B,其中 M=N=16M=N=16K=4K=4,元素类型为 FP32。假设输入 CC 矩阵为了简化起见包含零。我们将演示如何使用内在函数 __builtin_amdgcn_mfma_f32_16x16x4f32,该函数在一词调用中计算四个外积的和。此函数作用于单个矩阵块。

输入矩阵 AABB 的维度分别为 16×416\times44×164\times16,而矩阵 CCDD 包含 16×1616\times16 个元素,因此每个线程有 4 个元素需要存储,如图和代码片段所示。

以下两图显示 1) AABB 输入的形状和大小;以及 2) AABB 的元素如何映射到波前拥有的寄存器中的通道。

以下两图显示 1)输出矩阵 DD 的形状和大小;以及 2) DD 的元素如何映射到波前拥有的寄存器中的通道。

执行此 MFMA 操作的内核示例如下。

#define M 16
#define N 16
#define K 4
__global__ void sgemm_16x16x4(const float *A, const float *B, float *D)
{
using float4 = __attribute__( (__vector_size__(K * sizeof(float)) )) float;
float4 dmn = {0};
int mk = threadIdx.y + K * threadIdx.x;
int kn = threadIdx.x + N * threadIdx.y;
float amk = A[mk];
float bkn = B[kn];
dmn = __builtin_amdgcn_mfma_f32_16x16x4f32(amk, bkn, dmn, 0, 0, 0);
for (int i = 0; i < 4; ++i) {
const int idx = threadIdx.x + i * N + threadIdx.y * 4 * N;
D[idx] = dmn[i];
}
}

此内核的启动方式如下。

dim3 grid (1, 1, 1);
dim3 block(16, 4, 1);
sgemm_16x16x4 <<< grid, block >>> (d_A, d_B, d_D);

如前所述,输入 CC 矩阵假定包含零。

示例 2 – V_MFMA_F32_16x16x1F32

考虑矩阵乘法的场景,其维度为 M=N=16M=N=16K=1K=1,使用编译器内建函数 __builtin_amdgcn_mfma_f32_16x16x1f32。在这种情况下,输入值可以仅由 wavefront 的 16 个车道持有。事实上,此指令可以同时乘以 4 个此类矩阵,从而使每个车道持有这 4 个矩阵之一的值。

我们可以重用上一示例中的图来说明此操作的数据布局。在此情况下,输入 AA 不是一个 16×416 \times 4 矩阵,而是四个 16×116 \times 1 矩阵。但它们的布局方式,以及 wavefront 中每个车道拥有的元素是相同的。“列” AA 是独立的 16×116 \times 1 矩阵。输入 BB 类似。

!

!

给定矩阵乘法的输出具有与上一示例完全相同的数据布局。区别在于现在有四个独立的输出,每个输出对应一次乘法。

下面的内核展示了一个示例,用于对大小为 M=N=16M=N=16K=1K=1 的 4 个打包矩阵的批量乘法。

#define M 16
#define N 16
#define K 1
__global__ void sgemm_16x16x1(const float *A, const float *B, float *D)
{
using float16 = __attribute__( (__vector_size__(16 * sizeof(float)) )) float;
float16 dmnl = {0};
int mkl = K * threadIdx.x + M * K * threadIdx.y;
int knl = threadIdx.x + N * K * threadIdx.y;
float amkl = A[mkl];
float bknl = B[knl];
dmnl = __builtin_amdgcn_mfma_f32_16x16x1f32(amkl, bknl, dnml, 0, 0, 0);
for (int l = 0; l < 4; ++l) {
for (int i = 0; i < 4; ++i) {
const int idx = threadIdx.x + i * N + threadIdx.y * 4 * N + l * M * N;
D[idx] = dmnl[i];
}
}
}

该内核是通过以下方式启动的:

dim3 grid (1, 1, 1);
dim3 block(16, 4, 1);
sgemm_16x16x1 <<< grid, block >>> (d_A, d_B, d_D);

示例 3 – V_MFMA_F64_4x4x4F64

考虑 V_MFMA_F64_4x4x4F64 指令,它计算四个独立矩阵块的 MFMA,大小为 4×44\times4。执行的操作为 ZN=WNXN+YNZ_N = W_N X_N + Y_N,其中 WNW_NXNX_NYNY_NZNZ_N 都是大小为 4×44\times4 的矩阵,且 N=0,1,2,3N = 0,1,2,3

下面的两张图显示了 1)输入参数 AABB 的四个分量的尺寸和形状,以及 2)这些分量如何映射到 wavefront 所拥有的寄存器中的车道。此指令的参数包括 AABBCC 并返回 DD,因此我们知道每个参数和输出都包含 4 个矩阵。

!

!

输出 DD 和输入 CC 的布局与输入 BB 的布局相同。

关于 rocWMMA 的说明

我们只展示了三个利用 AMD Matrix Core 通过编译器内建函数进行运算的示例。更多示例可以在这里找到。请注意,内建函数在未来可能会发生变化,因此最好使用 AMD 的 rocWMMA C++ 库来加速混合精度 MFMA 操作。rocWMMA API 能够将矩阵乘累加问题分解为片段,并在跨 wavefront 并行分布的块状操作中使用它们。该 API 是一个 GPU 设备代码的头库,允许矩阵核心加速直接编译到您的内核设备代码中。这可以受益于编译器在生成内核汇编方面的优化。更多详细信息请参阅 rocWMMA 仓库

关于 AMD Matrix Instruction Calculator 工具的说明

对于那些想了解各种 MFMA 指令在 AMD Radeon™ 和 AMD Instinct™ 加速器上的性能,并希望了解矩阵元素与硬件寄存器之间映射关系的人,我们推荐 AMD Matrix Instruction Calculator 工具。这个强大的工具可以用来描述 WMMA 指令和给定架构的 MFMA ISA 级指令。我们欢迎社区 提交问题 和反馈。

参考文献

如果您有任何问题或评论,请在 GitHub 讨论区 与我们联系

Gina Sitaraman's avatar

Gina Sitaraman

通讯作者
Gina Sitaraman 是数据中心 GPU 软件解决方案部门的技术总监(SMTS)软件系统设计工程师。她获得了达拉斯德克萨斯大学计算机科学博士学位。她在地震数据处理领域拥有十多年的经验,开发并优化了使用 CPU 集群上的混合 MPI + OpenMP 以及 GPU 上的 CUDA 或 OpenCL 的预处理、迁移和后处理应用程序。她在 AMD 的时间主要用于解决在大型 HPC 集群上运行的科学应用程序的优化挑战。
Noel Chalmers's avatar

Noel Chalmers

作者
Noel Chalmers 是 AMD 数据中心 GPU 软件解决方案部门的高级技术员 (SMTS)。Noel 是 rocHPL 基准测试的主要开发者,rocHPL 是 AMD 对著名的 LINPACK 基准测试的优化实现,该基准测试在 ORNL 的 Frontier 超级计算机上实现了超过 1 EB/s 的性能。Noel 在滑铁卢大学获得了应用数学博士学位,在那里他研究了双曲系统上不连续伽辽金有限元方法的收敛性和稳定性。Noel 的研究兴趣包括高阶连续和不连续有限元方法以及大规模几何和代数多重网格方法的 GPU 加速。
Nicholas Malaya's avatar

Nicholas Malaya

作者
Nicholas Malaya 是 AMD 的一位院士,专注于软件开发、算法和高性能计算。他是 AMD 在百亿亿次计算应用性能方面的技术负责人,致力于确保工作负载在世界上最大的超级计算机上高效运行。Nick 的研究兴趣包括 HPC、计算流体动力学、贝叶斯推理以及机器学习/人工智能。他获得了德克萨斯大学的博士学位。在此之前,他在乔治城大学获得了物理学和数学双学位,并获得了 Treado 奖章。在他的大量业余时间里,他喜欢摩托车、长跑、葡萄酒以及与妻子和孩子们共度时光。
Damon McDougall's avatar

Damon McDougall

作者
Damon McDougall 是 AMD 数据中心 GPU 软件解决方案组的首席工程师。他获得了华威大学数学博士学位,并在 Oden 计算工程与科学研究所和德克萨斯高性能计算中心担任了六年的研究员。在 AMD,Damon 是 Frontier 中心卓越团队的一员,负责优化 AMD CPU 和 GPU 在百亿亿次计算方面的科学代码。他的专业兴趣包括高性能计算、大规模系统、不确定性量化、统计计算和科学软件开发。
Ossian O'Reilly's avatar

Ossian O'Reilly

作者
Ossian O'Reilly 是 AMD 数据中心 GPU 软件解决方案部门的技术人员 (MTS) 软件系统设计工程师。他致力于为 AMD GPU 移植和优化科学计算和工程应用程序。他拥有斯坦福大学地球物理学博士学位和瑞典林雪平大学计算数学博士学位。他的博士研究专注于用于包含摩擦界面和充满流体的裂缝的地震波传播的高阶数值方法,这些方法可应用于地震和火山学以及石油和天然气行业。作为博士后,他从事用于地形地震波传播的数值方法开发和分析,并针对 OLCF Summit 超级计算机实现了 GPU 模板内核。Ossian 的一些技术兴趣包括用于偏微分方程的高阶数值方法、基于模板和无矩阵方法,以及 GPU 内核开发和优化。
Rene Van Oostrum's avatar

Rene Van Oostrum

作者
René van Oostrum 是 AMD Research 的首席技术员工(PMTS)软件开发工程师。他获得了乌得勒支大学计算机科学博士学位,并在算法设计和分析方面有扎实的背景。在过去的十年里,他专注于 GPU 代码的实现和性能调优。他目前在为 AMD GPU 上的 HPC 工作负载开发性能分析工具。

Joseph Greathouse

审稿人
Joseph Greathouse 是 AMD AI GPU 软件组的院士,专注于 AMD Instinct 加速器和 ROCm 软件栈的性能和架构。在获得密歇根大学安娜堡分校计算机科学与工程博士学位后,他加入了 AMD Research,并一直在此公司工作。在所有这些职位中,他的工作涵盖了硬件与软件交互的各个方面。这包括设计用于加速软件开发的硬件特性、创建硬件优化的库优化以及硬件与软件协同设计以优化功耗。
© . This site is unofficial and not affiliated with AMD.