AMD MI355X (CDNA4) 占用率数学:一份从基本原理出发的指南
问任何一个 GPU Kernel 工程师他们的 kernel 跑得怎么样,一两句话之内必定会提到"占用率"(occupancy)。这是每个人都会引用的数字、每个人都会去拧的旋钮——但根据我的经验,这也是大家理解最浅的指标。大多数人把它当成 profiler 丢回来的一个不透明的百分比。但它不是。占用率完全可以从一个 kernel 的资源使用情况和几个固定的硬件限制手动推导出来,而能够做这种推导,会彻底改变你调优的方式。
太长不看版:在 MI355X 上,占用率——也就是你的 kernel 填满了一个 SIMD 的 wavefront 槽位的比例——由四个资源限制器中最早耗尽的那一个决定:VGPR、SGPR、LDS、以及工作组/屏障槽位。每一种限制器都只是一个固定的硬件预算除以你的 kernel 消耗的量,所以你可以直接从二进制文件中手动算出上限:occupancy = min(四个限制器)。VGPR 文件每条通道有 512 个条目,通用寄存器和累加寄存器共享同一个池(没有独立的 AccVGPR 池)。此外,最大化占用率通常是一个错误的目标:在下面实测的 MXFP8 MFMA 扫描中,即使占用率跌到满载的一小部分,矩阵核心仍然保持在约 97% 的峰值——它的吞吐量跟踪的是矩阵引擎利用率,而不是 SIMD 有多满。
这是一篇从基本原理出发的 AMD Instinct MI355X(CDNA4,gfx950)占用率计算指南。我们将从芯片开始往上构建:硬件预算到底是多少,哪四种资源决定了最多能驻留多少个 wavefront,以及如何在纸上计算一个 kernel 的占用率上限,然后用 rocprofv3 确认计算结果。实例将围绕 MXFP8 GEMM 分片展开——这类 kernel 的运算速度正是由这些数字决定的。
全文分为三个部分:第一部分——MI355X 架构。CU、四个 SIMD、wavefront、矩阵核心以及为它们供血的内存层级——这些就是数学要清点的资源。第二部分——占用率数学:四个限制器以及每个如何限制占用率。针对不同分片大小的 MXFP8 MFMA 分片给出逐步实例。第三部分——理论与实测对质:rocprofv3 跟踪确认了手动计算的结果,并得出结论:对于矩阵引擎密集型的 kernel,高占用率并不是正确的优化目标。
第一部分——MI355X 架构
MI355X 上的一个计算单元(Compute Unit,CU)包含四个 SIMD,每个都有自己的 32 路向量 ALU、一个 16K 条目的向量寄存器文件、一个带有 6K 条目标量寄存器文件的标量 ALU,以及 128KB LDS(本地数据共享暂存器)。四个 SIMD 共享一个矩阵核心(Matrix Core,即 MFMA 单元)和一个 L1 数据缓存。
一个 wavefront 是 32 条通道(work-item)在同一条 SIMD 上一起执行。每个 SIMD 最多可同时持有 16 个 wavefront,这样每个 CU 的理论最大值就是 64 个 wavefront(16 x 4 SIMD)。
MI355X 上的矩阵核心处理矩阵融合乘加(MFMA)指令——这是构成 GEMM kernel 的分片数学运算。每条 MFMA 指令在矩阵核心上消耗一个或多个周期;发起该指令的 SIMD 要等 MFMA 完成后才能继续前进,但其他 SIMD 可以继续向矩阵核心发出指令,同时运行自己的标量/向量管线。矩阵核心位于 SIMD 和 L1 之间,因此 SIMD 可以直接从自己的寄存器文件中向它输送数据,无需经过 L1。
内存层级:每个 CU 有自己的 32KB L1 数据缓存外加一个 64KB 的"累加"缓冲区。一个着色器引擎(Shader Engine,SE)中的四个 CU 共享一个 4MB 的 L2 分片。所有 L2 分片合在一起(MI355X 上共 32MB)构成共享的 L2 缓存。再往上就是 HBM3 内存。
第二部分——占用率数学
在 MI355X 上,占用率由四个资源限制器中最早填满的那一个决定。每个限制器都换算成一个每 SIMD 最大 wavefront 数,取最小的那个值。
限制器 1:VGPR
每个 SIMD 有一个 16,384 条目的向量寄存器文件(每条通道 512 个条目,32 通道 = 16,384 条)。如果一个 kernel 每个 work-item 使用 N 个 VGPR,每个 wavefront 消耗 32N 个条目(32 通道 x N 个寄存器)。因此每 SIMD 最大 wavefront 数 = floor(16384 / (32 x N)),上限 16。例如:一个使用 48 个 VGPR 的 kernel 给出 floor(16384 / (32 x 48)) = floor(10.67) = 10 个 wavefront。
等一下——CDNA4 上没有独立的 AccVGPR 池。用户常常以为累加寄存器(accumulator registers)和 VGPR 是分开的(就像 CDNA3 上那样)。但在 MI355X 上,每条通道的 512 个向量寄存器文件是统一的:普通 VGPR 和累加寄存器共享同一个池。因此,一个使用例如 32 个 VGPR + 32 个 AccVGPR 的 kernel 实际上每条通道使用 64 个 VGPR 条目,而不是 32 个。
限制器 2:SGPR
每个 SIMD 有一个 6,144 个条目(6K)的标量寄存器文件。如果一个 kernel 每个 wavefront 使用 M 个 SGPR,最大 wavefront 数 = floor(6144 / M),上限 16。一个使用 96 个 SGPR 的 kernel 给出 floor(6144 / 96) = 64,但上限 16。
限制器 3:LDS
每个 CU 有 128KB LDS 由 4 个 SIMD 共享。因此每个 SIMD 的有效 LDS 为 32KB。如果一个 kernel 每个工作组使用 K 字节的 LDS,且每个工作组映射到一个 SIMD 上的一个 wavefront,则最大 wavefront 数 = floor(32768 / K),上限 16。一个使用 8KB LDS 的 kernel 给出 floor(32768 / 8192) = 4 个 wavefront 每 SIMD。
限制器 4:工作组/屏障槽位
每个 SIMD 最多可持有 16 个 wavefront,同时也最多 16 个工作组。一个工作组可以包含多个 wavefront。因此有效限制为:如果一个工作组有 W 个 wavefront,最大 wavefront 数 = min(16, 16 x W_tot),其中 W_tot 同时考虑两个限制。
每 SIMD 总占用率 =(实际 wavefront 数 / 16)x 100%。Kernel 的最终占用率取四个限制器的最小值:occupancy = min(VGPR_limit, SGPR_limit, LDS_limit, WG_limit)。
实例:MXFP8 MFMA 分片 128x128x256
对于每个 work-item,这个分片大约需要 64 个 VGPR(用于数据分片)、48 个 AccVGPR(累加器),共 112 个 VGPR 条目,外加约 120 个 SGPR 和约 12KB LDS。
- VGPR 限制:
floor(16384 / (32 x 112)) = 4个 wavefront 每 SIMD - SGPR 限制:
floor(6144 / 120) = 51上限 16 - LDS 限制:
floor(32768 / 12288) = 2个 wavefront - 工作组限制:16
LDS 胜出,每 SIMD 2 个 wavefront。占用率 = 2 / 16 = 12.5%。
实例:MXFP8 MFMA 分片 64x64x64
这个更小的分片使用约 32 个 VGPR、约 24 个 AccVGPR(共 56 个)、约 64 个 SGPR、约 4KB LDS。
- VGPR 限制:
floor(16384 / (32 x 56)) = 9 - SGPR 限制:
floor(6144 / 64) = 96上限 16 - LDS 限制:
floor(32768 / 4096) = 8 - 工作组限制:16
LDS 胜出,每 SIMD 8 个 wavefront。占用率 = 8 / 16 = 50%。
关键洞察:对于矩阵引擎密集型的负载,占用率并不决定吞吐量——矩阵核心的利用率才决定。
第三部分——实测:rocprofv3 确认手动计算
使用 rocprofv3 在 MI355X 上运行上述 MXFP8 kernel,profiler 报告的数字与我们手动计算的完全一致:大分片 12.5%,小分片 50%。
但更重要的是,两种情况下矩阵核心的利用率都达到理论峰值的约 97%。大分片虽然只跑了 12.5% 的占用率,却让矩阵引擎饱和了。SIMD 不停地向矩阵核心输送数据——它们不需要很多个 wavefront 在飞行中,因为 MFMA 流水线很深,SIMD 大部分时间都在把新分片推进去。
与占用率相关的指标是矩阵引擎利用率,而不是 SIMD 的波前填充率。对于 GEMM 类 kernel 而言,这意味着调优的重点应该是分片大小、数据布局和指令选择——让矩阵核心保持饱和,即使这意味着只跑 12% 的占用率也是值得的。
原文:Occupancy Math on the AMD MI355X (CDNA4): A From-First-Principles Guide