前段时间社区的雾佬发了一篇使用 gem5 模拟 MI300X 的知乎,正好我最近在验证 AMDGPU 的浮点运算精度,就想着对比一下 gem5 MI300X 的 model 浮点精度和真实硬件有没有什么差异。
这里推荐使用服务器或者工作站来运行 gem5,个人电脑资源可能不太够用。
主要参考雾佬的文章,以及官方提供的文档 Full System AMD GPU model 来搭建。
基本思路是:
- 使用 qemu-system-x86_64 来制作包含 AMDGPU 驱动和 ROCm 环境的镜像,以及所需的内核;
- 使用 gem5 的 kvm 模式,模拟一个 x86 ,将 MI300X 的 model 作为 PCIe 卡集成进去;
- 最后使用 gem5 加载制作好的镜像和内核,就可以使用了。
为了方便,我使用 gem5 提供的 docker 环境来构建。
1docker pull ghcr.io/gem5/ubuntu-24.04_all-dependencies:v24-0
在本地拉取 gem5 相关仓库,并创建 gem5 的 docker 容器,然后将仓库所在目录共享给容器:
1git clone https://github.com/gem5/gem5.git
2git clone https://github.com/gem5/gem5-resources.git gem5/gem5-resources
3docker run --name gem5-amdgpu \
4--device /dev/kvm \
5--volume /home/zevorn/gem5:/gem5 \
6--privileged \
7-it ghcr.io/gem5/ubuntu-24.04_all-dependencies:v24-0
接下来就是编译环节,分两步。
首先在容器外面的 gem5/gem5-resources 仓库里制作需要的镜像和内核:
1cd gem5/gem5-resources/src/x86-ubuntu-gpu-ml/
2./build.sh
这里需要耐心等待,这个构建脚本会使用 packer 来制作镜像,制作成功以后,会生成以下文件:
1├── disk-image
2│ └── x86-ubuntu-gpu-ml
3├── vmlinux-gpu-ml
然后进入容器内,开始构建 gem5:
1cd /gem5
2scons build/VEGA_X86/gem5.opt -j$(nproc)
我们需要修改默认的 MI300X 的 python 文件,避免运行完退出:
1$ git diff configs/example/gpufs/mi300.py
2@@ -153,7 +153,7 @@ def runMI300GPUFS(
3 )
4 b64file.write(runscriptStr)
5
6- args.script = tempRunscript
7+ # args.script = tempRunscript
8
9 # Defaults for CPU
10 args.cpu_type = "X86KvmCPU"
在 gem5 源码路径下新建一个 pytorch_test.py 文件,用于 gem5 运行 mi300 时加载,但实际我们不会执行它:
1#!/usr/bin/env python3
2
3import torch
4
5x = torch.rand(5, 3).to('cuda')
6y = torch.rand(3, 5).to('cuda')
7
8z = x @ y
我们在容器内安装一个 tmux,方便启动 gem5 以后,通过另一个窗格连接进终端:
1apt update
2apt install tmux
然后启动试试:
1build/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:
1./util/term/gem5term localhost 3456
2root@gem5:~#
然后我们需要手动加载 AMDGPU 驱动,可以直接在 gem5 终端输入:
1export LD_LIBRARY_PATH=/opt/rocm/lib:$LD_LIBRARY_PATH
2export HSA_ENABLE_INTERRUPT=0
3export HCC_AMDGPU_TARGET=gfx942
4dmesg -n8
5cat /proc/cpuinfo
6dd if=/root/roms/mi300.rom of=/dev/mem bs=1k seek=768 count=128
我们来验证一下是否加载成功:
1root@gem5:~# rocminfo
2ROCk module version 6.12.12 is loaded
3=====================
4HSA System Attributes
5=====================
6Runtime Version: 1.15
7Runtime Ext Version: 1.7
8System Timestamp Freq.: 0.001000MHz
9Sig. Max Wait Duration: 18446744073709551615 (0xFFFFFFFFFFFFFFFF) (timestamp count)
10Machine Model: LARGE
11System Endianness: LITTLE
12Mwaitx: DISABLED
13XNACK enabled: NO
14DMAbuf Support: YES
15VMM Support: YES
16...
17*******
18Agent 2
19*******
20 Name: gfx942
21 Uuid: GPU-XX
22 Marketing Name: AMD Instinct MI300X
23 Vendor Name: AMD
24 Feature: KERNEL_DISPATCH
25 Profile: BASE_PROFILE
26 Float Round Mode: NEAR
27 Max Queue Number: 128(0x80)
28 Queue Min Size: 64(0x40)
29 Queue Max Size: 131072(0x20000)
30 Queue Type: MULTI
31 Node: 1
32 Device Type: GPU
33...
接下来就可以正常使用了。
我在测试 gem5 MI300X 的 fma 指令的运算结果是,和 cpu 以及 MI300X 真实硬件进行 bit 级对比后,发现总是相差 3~4 ulp,于是我阅读 fma 的源码,发现是 gem5 用乘加两步运算来模拟的,这导致了和真实硬件的浮点精度存在误差,于是我修改源码:
1$ git diff src/arch/amdgpu/vega/insts/instructions.hh
2diff --git a/src/arch/amdgpu/vega/insts/instructions.hh b/src/arch/amdgpu/vega/insts/instructions.hh
3index 7a328f9230..76bd96beee 100644
4--- a/src/arch/amdgpu/vega/insts/instructions.hh
5+++ b/src/arch/amdgpu/vega/insts/instructions.hh
6@@ -44352,8 +44352,9 @@ namespace VegaISA
7 int lane_A = i + M * (block + B * (k / K_L));
8 int lane_B = j + N * (block + B * (k / K_L));
9 int item = k % K_L;
10- result[i][j] +=
11- src0[item][lane_A] * src1[item][lane_B];
12+ result[i][j] =
13+ std::fma(src0[item][lane_A], src1[item][lane_B],
14+ result[i][j]);
15 }
16 }
17 }
之后测试就正常了,我将这笔 bugfix,贡献到了上游社区,目前已被合并:arch-vega: Improve MFMA precision to match MI300X hardware