ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

老卡焕新:AMD MI50手动编译ROCm 6.4跑大模型推理全指南

老卡焕新:AMD MI50手动编译ROCm 6.4跑大模型推理全指南 手里这块 AMD Instinct MI50严格来说已经不算新卡了。Vega 20 核心、gfx906 架构、16GB HBM2 显存放到今天是典型的“老将”。但正因为显存带宽仍然有 1TB/s 级别跑中等规模大模型推理其实相当能打。问题在于官方 ROCm 对 gfx906 的支持等级早就从“fully supported”一路掉到了“maintenance”甚至预编译包里经常找不到能用的 gfx906 代码块。为了在 Linux 上把它重新盘活我把 ROCm 6.4 从源码手动编译了一遍最终跑通了 7B 级别量化模型的本地推理。这篇文章就是这次折腾的完整笔记目标很直接给手里有 MI50 或同类老 AMD 计算卡、想在 Linux 上正经跑大模型的朋友一条可以照着走通的路。1. 项目背景与核心挑战1.1 MI50 的硬件底子和它在 2025 年的价值先简单回忆一下 MI50 的规格。它用的是 7nm Vega 20 核心60 个计算单元3840 个流处理器16GB HBM2 显存带宽约 1TB/s单精度浮点大约 13.3 TFLOPSFP16 算力约 26.5 TFLOPS。这个规格的亮点不是算力而是“显存带宽非常高、显存容量 16GB、二手价格还很便宜”。大模型推理里Transformer 解码是典型的 memory-bound 任务每生成一个 token 都要把整个模型的权重读一遍所以带宽比算力更值钱。这也是为什么一块老掉牙的 MI50 跑起量化模型来速度还能压过很多现代中端卡。但便宜和性能都不是白拿的。MI50 的架构代号是 gfx906而 ROCm 在 6.x 时代已经把 gfx906 从“推荐支持”降级成了“维护支持”。这意味着你直接装一个官方预编译的 ROCm 工具链很可能出现驱动能加载、rocm-smi 能看到卡但一跑实际的 HIP 程序就报错或者根本找不到适配 gfx906 的内置二进制。更麻烦的是不少新版本库为了减少体积和编译时间默认只打包 gfx1030、gfx1100 这类新架构老架构用户就得自己去源码里翻。1.2 为什么一定要手动编译 ROCm 6.4很多人会问直接用 Docker 镜像或者官方预编译 deb 包不好吗我的回答是能跑当然好但 MI50 用户遇上的情况往往不允许你这么省事。预编译包的典型问题有三类第一官方包内置的 gfx906 kernel image 经常缺失或版本不匹配哪怕安装成功运行时也会报出类似 “no suitable kernel blobs” 的提示第二打包的构建时间和当前内核版本、libdrm 版本互相绑架Ubuntu 升级内核后 DKMS 和 amdgpu 驱动很容易一起翻车第三ROCr、HIP、rocBLAS 这些组件的二进制彼此绑定你想单独修一个组件都无从下手。手动编译的好处就在于可控。你可以显式指定AMDGPU_TARGETSgfx906让整个工具链只为中心架构生成代码你可以选择只编译大模型推理真正依赖的那几个仓库避开 Tensile 全架构扫描这种耗时大户你还可以把编译产物固定在自己的/opt/rocm目录里内核升级后不至于整个环境报废。当然代价也明显编译时间长、依赖关系易错、出问题不好排查。我把三条常见路线对比过路线优点缺点官方预编译 deb/rpm 包安装简单组件齐全gfx906 支持缺失依赖冲突多内核升级易挂Docker 镜像环境隔离官方维护性能有轻微损耗GPU 透传依赖宿主机驱动完整源码手动编译完全可控可指定 gfx906内核和库版本自由搭配编译时间长对使用者的 Linux 功底有要求综合来看如果你只是短期体验Docker 也能凑合但如果打算长期把 MI50 当作稳定的本地推理卡手动编译几乎是绕不开的正路。2. 环境准备与架构认知2.1 硬件平台和操作系统的选型在开始编译之前我建议先把自己的平台理清楚。MI50 是 PCIe 4.0 x16 接口的卡理论上插 PCIe 3.0 也能用但带宽会砍半跑大模型时的 token 生成速度会跟着掉。我自己是插在一台 X570 主板上配了 Ryzen 5900X内存 64GB。没错内存大一点很重要后面编译 LLVM 和 Tensile 的时候16GB 物理内存会非常吃力32GB 才算舒服。操作系统我强烈建议用 Ubuntu 22.04 LTS内核保持在 5.15 或 6.5 系列。ROCm 6.4 对内核版本有明确的匹配要求太新的内核比如 6.8容易出现 amdgpu 模块和用户态 ROCm 版本不一致的问题太老的内核又可能缺少新特性。如果你用其他发行版比如 Arch 或者 Fedora也可以但需要自己编译匹配的 DKMS 模块维护成本会明显高一个档次。另外务必在 BIOS 里开启 Above 4G Decoding 和 Resizable BAR这能减少 GPU 直接访问大块显存时的寻址问题。2.2 ROCm 软件栈的核心模块与依赖顺序手动编译最容易被劝退的地方就是 ROCm 不是一个单一的软件而是一整条工具链。它的核心模块包括ROCr RuntimeHSA 的用户态运行时负责和内核驱动通信、管理队列和内存是所有上层库的地基。ROCm-LLVMAMD 定制的 LLVM 分支负责生成 AMDGPU 后端机器码。HIP 编译器 hipcc 底层就是调用它。HIPAMD 对标 CUDA 的异构编程接口不装它PyTorch 和 llama.cpp 都没法调用 GPU。rocBLAS / rocFFT / rocSPARSEAMD 的 BLAS 和 FFT 等高性能数学库PyTorch 等机器学习框架依赖它们做矩阵运算。关键的一点是必须按照“ROCr - LLVM - HIP - rocBLAS”的顺序编译因为每一层都依赖前一层的头文件和动态库。如果顺序反了cmake 在检测依赖时会直接报错。我第一次图省事跳过 ROCI 的 Thunk 层结果编译 ROCr 的时候死活过不去后来才发现 Thunk 接口必须先编译好。2.3 获取源码与安装基础依赖基础依赖方面Ubuntu 22.04 上建议先执行sudo apt update sudo apt install -y git cmake build-essential python3-dev \ libssl-dev libelf-dev libdrm-dev libnuma-dev libpciaccess-dev \ libxml2-dev llvm-dev lld clang pkg-config如果你是全新系统还要装一个libstdc-12-dev避免编译时头文件版本太低。源码获取上我不建议直接 clone 整个 ROCm 超仓库太大了。比较清晰的做法是分别 clone 需要的仓库比如git clone -b rocm-6.4.x --depth 1 https://github.com/ROCm/ROCm-OpenCL-Runtime.git git clone -b rocm-6.4.x --depth 1 https://github.com/ROCm/HIP.git git clone -b rocm-6.4.x --depth 1 https://github.com/ROCm/ROCm-LLVM.git这里有一个经验用--depth 1做浅克隆能省掉大量历史提交记录网络不好时尤其重要。真正需要看历史的时候再单独解除深度限制也不迟。3. 手动编译核心流程3.1 编译前必须搞懂的架构变量与环境变量手动编译 ROCm第一个要刻进脑子里的概念是架构代号。MI50 是 Vega 20对应 LLVM/AMDGPU 后端里的gfx906。很多工程的 cmake 默认会编译一堆目标架构非常浪费时间所以要在编译前显式告诉构建系统export AMDGPU_TARGETSgfx906这个变量最好在编译所有组件之前设置并且写进~/.bashrc里因为你后续编译 llama.cpp、调用 PyTorch 时也可能会用到。另一个容易踩坑的环境变量是HSA_OVERRIDE_GFX_VERSION。它的作用很神奇强制 HSA 运行时把当前 GPU 报告成另一个架构从而绕过某些库没有为 gfx906 内置二进制的问题。比如想骗过系统让它以为自己是 Vega 10gfx900就是export HSA_OVERRIDE_GFX_VERSION9.0.0有些场景下需要设成9.0.6。但这个东西不是万能药设错会导致程序启动后随机报错甚至输出乱码。我自己的建议是一开始先不设如果运行时明确提示找不到 gfx906 的 code object 再设并且优先试9.0.0。3.2 按依赖顺序完成组件构建真正开始编译时我推荐优先使用 ROCm 官方提供的 rbuild 工具而不是直接敲 cmake。rbuild 会自动绑定各个仓库的依赖分支保证版本匹配。安装 rbuild 后一种常见的用法是这样的rbuild build -d ~/rocproj --build-dir ~/rocproj-build \ --cmake-arg -DAMDGPU_TARGETSgfx906 --cuda 21 | tee build.log这条命令会按依赖关系依次构建 ROCm 的核心组件你基本不用操心仓库之间的版本对齐问题。如果你不用 rbuild手写的话至少要经历先编译 ROCm-Thunk-Interface再到 ROCm-Runtime再到 HIP最后才是 rocBLAS 这类上层库。每一个仓库内部的套路都差不多核心就是cmake -B build -DCMAKE_BUILD_TYPERelease -DAMDGPU_TARGETSgfx906 .. cmake --build build -j$(nproc) sudo cmake --install build需要特别提醒的是 rocBLAS。它在构建时会调用 Tensile 做架构扫描和 GEMM 内核调优这个过程极其耗时曾经害得我等了整整一个晚上。如果你不想要这么大的工程可以在 cmake 参数里加-DTENSILE_ARCHgfx906来缩小范围或者干脆-DTensile_CPU_THREADS$(nproc)提高并行度。但千万别直接禁用 Tensile因为大模型推理最依赖的就是 GEMM 运算禁用以后速度和稳定性都会受影响。3.3 安装后的环境配置与验证编译安装完成后ROCm 默认会被安装到/opt/rocm。这时候你需要把它加入 PATH 和库搜索路径export ROCM_PATH/opt/rocm export PATH$ROCM_PATH/bin:$PATH export LD_LIBRARY_PATH$ROCM_PATH/lib:$LD_LIBRARY_PATH然后先做两个最基础的验证。第一是用rocminfo查看 GPU 信息rocminfo | grep -E Name:|Marketing|gfx906如果能看到类似Agent 2: AMD Instinct MI50的信息说明 ROCr 和驱动已经正常工作了。第二是用hipcc编译一个最简单的 HIP 程序确认 HIP 编译器没有缺件#include hip/hip_runtime.h #include cstdio int main() { hipDeviceProp_t prop; hipGetDeviceProperties(prop, 0); printf(Device: %s\n, prop.name); return 0; }hipcc hello_hip.cpp -o hello_hip ./hello_hip能打出显卡名字就说明手动编译这关你已经过了大半。顺便检查一下内核驱动是不是还在lsmod | grep amdgpu如果amdgpu模块没加载后面一切都白搭。4. 大模型推理实战从框架选择到参数调优4.1 框架路线怎么选llama.cpp 还是 PyTorchROCm 环境就绪后就可以开始考虑怎么跑大模型了。MI50 上有两条主流路线一条是 llama.cpp另一条是 PyTorch ROCm 版 Hugging Face transformers。llama.cpp 的优势在于依赖极少、编译简单、对老显卡很友好。它直接通过 GGML_HIPON 来启用 ROCm 后端用 GGUF 格式的量化模型不需要装庞大的 PyTorch 全家桶。PyTorch 路线则更适合想跑 Hugging Face 生态里各种现成模型、要灵活改推理脚本的朋友但为 gfx906 内置的支持在 PyTorch 官方 wheel 里也若有若无老架构用户偶尔会被 FlashAttention 之类的算子劝退。我实际推荐先用 llama.cpp理由有三条第一它把 kernel 生成和内存管理都封装得比较简单出现问题时容易定位第二它的 CPU 和 GPU 混跑机制非常成熟即使显存不够也能自动把部分层放到 CPU 上第三配合 GGUF 量化格式16GB 显存正好能覆盖 7B 到 14B 参数量的主流模型。路线安装难度模型格式老卡兼容性推理速度llama.cpp低GGUF需自行转换或下载很友好可明显指定 gfx906中高受带宽限制PyTorch Transformers中高Hugging Face 权重占用更大依赖算子兼容性偶尔要设 override中灵活性高4.2 模型选型与显存估算16GB 到底能跑什么选模型之前先学会估算显存占用。一块 7B 参数的模型如果使用 Q4_K_M 量化权重大约 4.4GBKV cache 按照 4096 上下文、GQA 结构来算一般在 1GB 到 2GB 之间再加上 CUDA/HIP 上下文、输入输出缓存和系统预留总共大约需要 8GB 到 10GB。这个容量对 16GB 显存来说非常舒服甚至可以开到 32K 长上下文。如果是 14B 级别的模型量化到 Q4权重会涨到接近 9GB加上 KV cache 后基本贴着 14GB 左右的线跑 8K 上下文就是极限了。再往上比如 32B 参数的 Q4 模型就算权重压到 20GB 左右也超出了单卡容量只能走 CPU offload速度会明显下降。所以 MI50 最舒服的区间是“7B 到 14B 的量化模型”。我测试过 Qwen2.5-7B-Instruct 和 Llama-3-8B-Instruct 的 GGUF 版本都能稳定跑完整生成流程。如果你喜欢更强壮的小模型也可以考虑 Qwen2.5-14B 的 Q4_K_M但上下文别给太长。4.3 llama.cpp 编译与关键参数调优为了让 llama.cpp 启用 ROCm 后端编译前必须设置正确的架构变量git clone https://github.com/ggml-org/llama.cpp.git cd llama.cpp cmake -B build -DGGML_HIPON -DAMDGPU_TARGETSgfx906 -DCMAKE_BUILD_TYPERelease cmake --build build -j$(nproc)这里顺带提一句如果你在编译过程中得到和 gfx906 相关的致命错误试着在编译前执行export HSA_OVERRIDE_GFX_VERSION9.0.6然后再重新配置。很多老卡用户靠这一招就救回来了。模型下载或者转换好之后运行命令可以这样写./build/bin/llama-cli -m /models/qwen2.5-7b-instruct-q4_k_m.gguf \ --n-gpu-layers 99 -t 8 -c 4096 -p 你好介绍一下你自己。--n-gpu-layers 99表示尽可能把更多层放到 GPU 上对内存带宽比较大的 MI50 来说是正确姿势。-t 8是 CPU 线程数因为在解码阶段 CPU 还会承担一些预处理和采样工作设太少会拖后腿。-c 4096是上下文长度如果显存吃紧可以降到 2048。运行过程中用另一个终端看 GPU 状态rocm-smi --showuse --showmeminfo vram你会看到显存占用率、GPU 利用率、温度和功耗。如果显存占用长期超过 90%说明模型加上 KV cache 已经逼近极限建议减少上下文长度或者把--n-gpu-layers调小让一部分层留在 CPU防止 OOM 后系统卡死。5. 常见问题与排查技巧实录5.1 编译阶段报错对照速查表手动编译最难受的就是报错看不懂。我把这次过程中遇到的典型报错整理成了一个小表希望能帮你少走弯路报错现象常见原因解决办法Could NOT find ROCmcmake 找不到/opt/rocm显式设置-DROCM_PATH/opt/rocm或添加export ROCM_PATH/opt/rocmUnsupported gpu arch: gfx906编译器版本太老或者没有为 gfx906 开目标使用 AMD 定制的 ROCm LLVM并通过AMDGPU_TARGETSgfx906明确指定Tensile 阶段编译到一半崩溃内存不足加 swap或者用TENSILE_ARCHgfx906缩小搜索范围链接时找不到libamdhip64.soHIP 还没编译或者 LD_LIBRARY_PATH 没设置先确认 HIP 已 install再检查ldconfig和 LD_LIBRARY_PATH编译进程被 OOM Killer 杀掉并行度过高、内存不够改用-j4而不是$(nproc)或增加 swap 空间这里最值得展开说一下的是内存问题。LLVM 和 Tensile 都属于“内存狂魔”LLVM 在链接 phase 偶尔会吃掉 12GB 以上Tensile 在生成架构代码时也会并行开很多线程。如果你的机器只有 16GB 内存编译时最好控制并发数并且准备一个 20GB 以上的 swapfilesudo fallocate -l 20G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile5.2 运行时常见故障和排查逻辑编译过了之后真正跑大模型时也会有一堆“惊喜”。最典型的是运行任何 HIP 程序就报HSA_STATUS_ERROR_MEMORY_FAULT或者HSA_STATUS_ERROR_EXCEPTION。出现这类错误第一时间查HSA_OVERRIDE_GFX_VERSION设了没有、设成什么了。如果你设了9.0.6但程序不稳定改成9.0.0看看。这个变量是为绕过旧架构兼容性问题设计的但它同时会改变内存分配和队列管理的逻辑不是随便设就能跑通。还有一类问题是启动时出现amdgpu: failed to initialize VM buffer之类的内核日志。这通常是内核模块 amdgpu 和用户态 ROCr 版本不匹配造成的。解决方法是使用 DKMS 重新编译匹配当前内核版本的驱动模块或者干脆回退到你编译 ROCr 时的内核版本。最后是性能相关的坑。有时候你会发现自己明明把层都塞进了 GPU速度却和 CPU 差不多。这种时候先看rocm-smi里的 GPU 利用率如果只跑在 30%-40%说明 kernel 没有真正调度起来多半是HSA_OVERRIDE_GFX_VERSION或者 llama.cpp 的 offload 层数不够如果 GPU 利用率 95% 以上但 token 速度只有个位数那就是显存带宽已经打满了MI50 的 1TB/s 带宽就这个上限。5.3 我踩过的三个记忆深刻的坑第一个坑是不该无脑 clone 整个 ROCm 超仓库。超仓库会一次性拉下来二十多个子模块很多根本用不上。如果你的目标是跑大模型没有必要把 MIOpen、MIGraphX 这些推理优化库也从头编译它们依赖额外工具链编译时间以天为单位。按需克隆 HIP Runtime 加 rocBLAS 就能解决绝大部分推理需求。第二个坑是乱设HSA_OVERRIDE_GFX_VERSION。我第一次跑 Qwen 的时候按照网上某个教程把它设成了9.0.6结果模型加载倒是正常但生成到一半突然输出一堆乱码。排查了很久才发现是这个变量在捣鬼。后来在 llama.cpp 的官方 issue 里看到对 MI50 来说9.0.0更接近原始 gfx900 的行为模式改回来之后问题消失。这个经验分享给大家编码解码类问题先从架构覆盖开始怀疑。第三个坑是显存占用没留余量导致整机 OOM。一台机器如果物理内存只有 32GBGPU 又占走 16GB 显存而你在映射统一内存时还开了一堆缓存系统很容易直接卡死。我的补救办法是尽量用 llama.cpp 的--mlock参数把模型权重页锁在物理内存里避免被换到磁盘同时 PyTorch 里要限制PYTORCH_HIP_ALLOC_CONF和max_split_size_mb防止显存碎片化导致分配失败。6. 性能实测与后续维护经验6.1 我这里的运行速度和影响因素手动编译完 ROCm 6.4 并把 llama.cpp 配置好后我简单做过一轮速度体验。用 Qwen2.5-7B-Instruct 的 Q4_K_M 版本GPU 加载全部层上下文设置为 4096单 batch 连续生成大约稳定在 7 到 10 token/s 之间。换成 Llama-3-8B-Instruct 的 Q4_K_M数字稍有波动也在相似范围内。如果尝试 14B 参数的量化模型速度会掉到 4 到 7 token/s 左右原因主要是 14B 权重接近 9GBKV cache 进一步挤占带宽同时计算密度也变高了。这个速度虽然不能和 4090 比但作为一块二手市场的便宜计算卡已经很惊喜。CPU-only 跑 7B Q4 通常只有 1 到 3 token/sMI50 至少是数量级的提升。影响速度的核心变量有三个一是 GPU 运行频率长时间满载后温度超过 85 度会掉频二是 offload 层数建议尽量全部放到 GPU否则跨 PCIe 拷贝权重的开销很大三是上下文长度上下文越长 KV cache 越大带宽越紧token 速度也会微降。6.2 什么场景适合 MI50什么场景别为难它以我这一段时间的使用感受MI50 最适合的场景是本地私人助理、代码补全、离线知识库问答这类“单用户、低并发、模型 7B 到 14B”的推理任务。因为它的带宽优势能直接转化为较快的单 token 延迟而且 16GB 显存对量化模型很友好。但不适合的场景也很明显大规模并发服务、长上下文聊天、以及任何形式的模型微调训练。vLLM 这类面向高并发推理的框架对 gfx906 基本已经放弃优化强行使用会遇到大量算子缺失训练更不用想FP16 算力 26.5 TFLOPS 在当年就不算出彩放到今天连入门训练卡都算不上。所以如果你买 MI50 是为了跑本地推理方向是对的如果想搞分布式训练或高并发 API 服务还是去看更新的卡吧。6.3 内核升级后如何保住系统ROCm 手动编译环境最脆弱的环节就是内核升级。amdgpu内核模块和用户态 ROCr 之间没有强绑定但 DKMS 模块在每次内核升级后都需要重新编译。一个偷懒但有效的办法是锁定内核版本sudo apt-mark hold linux-image-$(uname -r) sudo apt-mark hold linux-headers-$(uname -r)这样系统不会因为在升级时顺手把内核换掉导致驱动失配。如果你想升级内核那就重新去对应仓库编译一遍 DKMS 模块或者干脆忍受一次完整重编译。手动编译的产物建议备份到独立目录比如/opt/rocm-6.4-gfx906方便以后随时切换或回滚。最后分享一个实用教训如果你看中的只是“能在 MI50 上跑大模型”其实可以先试试官方预编译包加 llama.cpp给自己 30 分钟时间验证一旦遇到莫名其妙的底层错误再转向手动编译。手动编译这条路的收益是稳定和可控但代价是时间和耐心。等你真的跑通那一刻再把rocm-smi里看到的 16GB HBM2 显存利用图截下来就会觉得前面熬的夜都值了。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进