ZEVORN.blog

July 28, 2025

使用 gem5 模拟 MI300X,立省 10 万块?

article4.3 min to read

前段时间社区的雾佬发了一篇使用 gem5 模拟 MI300X 的知乎,正好我最近在验证 AMDGPU 的浮点运算精度,就想着对比一下 gem5 MI300X 的 model 浮点精度和真实硬件有没有什么差异。

这里推荐使用服务器或者工作站来运行 gem5,个人电脑资源可能不太够用。

主要参考雾佬的文章,以及官方提供的文档 Full System AMD GPU model 来搭建。

基本思路是:

  1. 使用 qemu-system-x86_64 来制作包含 AMDGPU 驱动和 ROCm 环境的镜像,以及所需的内核;
  2. 使用 gem5 的 kvm 模式,模拟一个 x86 ,将 MI300X 的 model 作为 PCIe 卡集成进去;
  3. 最后使用 gem5 加载制作好的镜像和内核,就可以使用了。

为了方便,我使用 gem5 提供的 docker 环境来构建。

docker pull ghcr.io/gem5/ubuntu-24.04_all-dependencies:v24-0

在本地拉取 gem5 相关仓库,并创建 gem5 的 docker 容器,然后将仓库所在目录共享给容器:

git clone https://github.com/gem5/gem5.gitgit clone https://github.com/gem5/gem5-resources.git gem5/gem5-resourcesdocker run --name gem5-amdgpu \--device /dev/kvm \--volume /home/zevorn/gem5:/gem5 \--privileged \-it ghcr.io/gem5/ubuntu-24.04_all-dependencies:v24-0 

接下来就是编译环节,分两步。

首先在容器外面的 gem5/gem5-resources 仓库里制作需要的镜像和内核:

cd gem5/gem5-resources/src/x86-ubuntu-gpu-ml/./build.sh

这里需要耐心等待,这个构建脚本会使用 packer 来制作镜像,制作成功以后,会生成以下文件:

├── disk-image│   └── x86-ubuntu-gpu-ml├── vmlinux-gpu-ml

然后进入容器内,开始构建 gem5:

cd /gem5scons build/VEGA_X86/gem5.opt -j$(nproc)

我们需要修改默认的 MI300X 的 python 文件,避免运行完退出:

$ git diff configs/example/gpufs/mi300.py@@ -153,7 +153,7 @@ def runMI300GPUFS(         )         b64file.write(runscriptStr)-    args.script = tempRunscript+    # args.script = tempRunscript     # Defaults for CPU     args.cpu_type = "X86KvmCPU"

在 gem5 源码路径下新建一个 pytorch_test.py 文件,用于 gem5 运行 mi300 时加载,但实际我们不会执行它:

#!/usr/bin/env python3import torchx = torch.rand(5, 3).to('cuda')y = torch.rand(3, 5).to('cuda')z = x @ y

我们在容器内安装一个 tmux,方便启动 gem5 以后,通过另一个窗格连接进终端:

apt updateapt install tmux

然后启动试试:

build/VEGA_X86/gem5.opt configs/example/gpufs/mi300.py --disk-image gem5-resources/src/x86-ubuntu-gpu-ml/disk-image/x86-ubuntu-gpu-ml --kernel gem5-resources/src/x86-ubuntu-gpu-ml/vmlinux-gpu-ml --app pytorch_test.py

我们使用tmux,在当前窗口再新建一个终端,然后使用下面的命令连接到 gem5:

./util/term/gem5term localhost 3456root@gem5:~#

然后我们需要手动加载 AMDGPU 驱动,可以直接在 gem5 终端输入:

export LD_LIBRARY_PATH=/opt/rocm/lib:$LD_LIBRARY_PATHexport HSA_ENABLE_INTERRUPT=0export HCC_AMDGPU_TARGET=gfx942dmesg -n8cat /proc/cpuinfodd if=/root/roms/mi300.rom of=/dev/mem bs=1k seek=768 count=128

我们来验证一下是否加载成功:

root@gem5:~# rocminfoROCk module version 6.12.12 is loaded=====================HSA System Attributes=====================Runtime Version:         1.15Runtime Ext Version:     1.7System Timestamp Freq.:  0.001000MHzSig. Max Wait Duration:  18446744073709551615 (0xFFFFFFFFFFFFFFFF) (timestamp count)Machine Model:           LARGESystem Endianness:       LITTLEMwaitx:                  DISABLEDXNACK enabled:           NODMAbuf Support:          YESVMM Support:             YES...*******Agent 2*******  Name:                    gfx942  Uuid:                    GPU-XX  Marketing Name:          AMD Instinct MI300X  Vendor Name:             AMD  Feature:                 KERNEL_DISPATCH  Profile:                 BASE_PROFILE  Float Round Mode:        NEAR  Max Queue Number:        128(0x80)  Queue Min Size:          64(0x40)  Queue Max Size:          131072(0x20000)  Queue Type:              MULTI  Node:                    1  Device Type:             GPU...

接下来就可以正常使用了。

我在测试 gem5 MI300X 的 fma 指令的运算结果是,和 cpu 以及 MI300X 真实硬件进行 bit 级对比后,发现总是相差 3~4 ulp,于是我阅读 fma 的源码,发现是 gem5 用乘加两步运算来模拟的,这导致了和真实硬件的浮点精度存在误差,于是我修改源码:

$ git diff src/arch/amdgpu/vega/insts/instructions.hhdiff --git a/src/arch/amdgpu/vega/insts/instructions.hh b/src/arch/amdgpu/vega/insts/instructions.hhindex 7a328f9230..76bd96beee 100644--- a/src/arch/amdgpu/vega/insts/instructions.hh+++ b/src/arch/amdgpu/vega/insts/instructions.hh@@ -44352,8 +44352,9 @@ namespace VegaISA                         int lane_A = i + M * (block + B * (k / K_L));                         int lane_B = j + N * (block + B * (k / K_L));                         int item = k % K_L;-                        result[i][j] +=-                          src0[item][lane_A] * src1[item][lane_B];+                        result[i][j] =+                            std::fma(src0[item][lane_A], src1[item][lane_B],+                                     result[i][j]);                     }                 }             }

之后测试就正常了,我将这笔 bugfix,贡献到了上游社区,目前已被合并:arch-vega: Improve MFMA precision to match MI300X hardware