前言最近干了一件事: 把一套原本跑在 NVIDIA A100 上的训练和推理代码整个儿搬到了 AMD Instinct™ MI300X ROCm™ 上。说实话动工之前我也犹豫过——毕竟换平台听着就像给自己找麻烦。但真走完一遍才发现没有想象中那么折腾。这篇就把过程里的坑和经验摊开讲讲希望你看完能少绕点弯路。文末我放了个200 小时免费云算力的领取入口看完正好拿去实测。一、先放下一个老偏见不少人一提 ROCm第一反应还是生态弱、文档少、容易踩雷。这话放在几年前没毛病但以ROCm 6.x为节点情况真不一样了:PyTorch 官方已经发了 ROCm 原生版本不是社区魔改的那种vLLM、SGLang、TGI 这些主流推理框架基本都适配了MI300X 单卡就有 192GB 显存跑 70B 甚至更大的模型几乎不用纠结怎么切分。所以这次迁移我给自己定了个最关键的心法:把 “CUDA” 当成一个可以换的后端而不是绑死的运行时。PyTorch 早就帮你把这层抽象掉了。真正要动手改的地方主要是环境和个别底层算子其余的代码基本不用动。二、第一步:换镜像别跟驱动死磕我的第一条建议——千万别在宿主机上手动装驱动。装到怀疑人生不说版本一冲突能查半天。直接用官方 Docker 镜像干净利落:# ROCm 6.2 PyTorch 2.4 官方镜像dockerpull rocm/pytorch:rocm6.2_ubuntu22.04_py3.11_pytorch_release-2.4.0dockerrun-it--device/dev/kfd--device/dev/dri\--group-addvideo--ipchost --cap-addSYS_PTRACE\-v$(pwd):/workspace rocm/pytorch:rocm6.2_ubuntu22.04_py3.11_pytorch_release-2.4.0进容器后先做个最简单的冒烟测试确认显卡被认出来了:importtorchprint(torch.__version__)# 2.4.0rocm62print(torch.cuda.is_available())# True —— 没看错ROCm 下也是 Trueprint(torch.cuda.device_count())# 你的卡数print(torch.cuda.get_device_name(0))# AMD Instinct MI300X这里有个反直觉、但故意这么设计的点: ROCm 版的 PyTorch 保留了torch.cuda这个名字。也就是说你以前写的成千上万行x.cuda()、model.to(cuda)一行都不用改——cuda在这里其实就是 AMD 显卡(HIP 设备)的别名。光这一点就帮你省下了大把改代码的时间。三、监控命令也得换一套原来天天敲的nvidia-smi到 AMD 这边要换成rocm-smi。我做了张对照表建议直接存下来:你想干啥NVIDIAAMD / ROCm看显卡和显存nvidia-smirocm-smi持续刷新watch -n1 nvidia-smirocm-smi --watch看进程占用nvidia-smi pmonrocm-smi --showpid看多卡拓扑nvidia-smi topo -mrocm-smi --showtopo详细诊断nvidia-smi -qrocm-smi --showall记不住没关系关键盯住两个数:VRAM%(显存占比) 和MEM-USE(实际显存)对应的就是以前nvidia-smi里的Memory-Usage。四、训练代码: 九成情况直接能跑举个最常见的分布式训练例子。原来的 CUDA 写法长这样:modelMyModel().cuda()modeltorch.nn.parallel.DistributedDataParallel(model,device_ids[local_rank])搬到 ROCm 上原样跑就行。要留意的只有两个小细节:一是启动方式。torchrun直接能用但如果你以前依赖的是apex做分布式建议换成原生的DDP torchrun因为 apex 在 ROCm 上支持不太好。二是别纠结逐位数值对齐。换了平台浮点累加顺序变了单个 tensor 的数值会有微小差异。验收的时候别去逐个比对看最终的 loss 和 accuracy 曲线是不是在一条收敛轨迹上就行。至于偶尔冒出来的 “operator not implemented” 报错别慌——大概率是下面第五节要讲的情况。五、真正要改代码的地方: 自定义算子前面几节都在说基本不用改那到底什么时候必须动手?答案是:你自己写的 CUDA Kernel。好消息是迁移规律特别机械几乎就是一套查找替换:// 原来(CUDA)#includeATen/cuda/CUDAContext.hat::cuda::CUDAGuardguard(device);my_kernelgrid,block,0,at::cuda::getCurrentCUDAStream()(x,y,n);// 现在(ROCm / HIP)#includeATen/hip/HIPContext.hat::hip::HIPGuardguard(device);my_kernelgrid,block,0,at::hip::getCurrentHIPStream()(x,y,n);说白了就是把名字里的cuda换成hip:CUDAHIP / ROCmcudahipATen/cuda/ATen/hip/CUDAGuardHIPGuardcudaMallochipMalloccudaMemcpyhipMemcpy懒得一个个换?有两个偷懒办法。第一个写段宏让同一份代码在两边都能编译:#ifdef__HIP_PLATFORM_AMD__#defineCUDA_PREFIX(name)hip##name#else#defineCUDA_PREFIX(name)cuda##name#endif第二个更省事——AMD 自带一个hipify-perl工具一条命令就能把整个文件批量转好:hipify-perl my_kernel.cumy_kernel.hip六、推理更省心: 直接上 vLLM训练搞定了推理这块反而更简单。vLLM 在 ROCm 上已经是一等公民级别的支持一条命令就能把服务拉起来:# 单卡跑 Qwen2.5-72BMI300X 显存完全装得下vllm serve Qwen/Qwen2.5-72B-Instruct\--tensor-parallel-size1\--gpu-memory-utilization0.9\--max-model-len32768调优的时候我主要盯这三个地方:显存利用率可以大胆调高MI300X 显存大给到 0.9 甚至 0.95 都没问题偶尔遇到 attention 算子不稳定试试关掉 triton 版本的 FA(设VLLM_USE_TRITON_FLASH_ATTN0)换成 CK(Composable Kernel) 通常更稳跑长文本时先开--enforce-eager建一条基准线再逐步打开图优化。这样万一性能退化了你能一眼定位是哪步的锅。七、迁移完不算完得拿数据说话搬到新平台不能凭感觉说差不多了得摆数据。我一般会整理这么一张对比表(同等 batch、同代显卡):指标A100 (CUDA)MI300X (ROCm)怎么看训练吞吐(samples/s)基准x%看显存吃满后的步进推理吞吐(tok/s)基准x%重点看长文本的下限显存占用基准-x%MI300X 大显存的主场冷启动时间基准≈框架加载差距通常很小这里有个特别常见的坑: 第一次跑发现 ROCm 比 CUDA 慢立马下结论AMD 不行。其实很多时候慢只是因为第一次在编译 kernel第二次开始就正常了。所以计时之前一定记得先 warm-up 跑几轮。八、报错速查表(收藏备用)整理了几类高频报错建议存着遇到了直接对号入座:报错关键字啥原因怎么解决MIOpen(HIP)相关卷积 kernel 没找到设MIOPEN_FIND_MODE3走启发式hipErrorNoBinaryForGpu镜像和显卡架构对不上按卡型加HSA_OVERRIDE_GFX_VERSION9.4.2peer access is not available多卡之间没法直连检查--device/dev/kfd和 IOMMU 设置显存报 OOM但rocm-smi显示没用满显存碎片 / PyTorch 缓存设PYTORCH_HIP_ALLOC_CONFgarbage_collection_threshold:0.6九、写在最后整条路走下来最大的感受是:真正要改代码的部分可能还不到 5%。剩下 95%都是换换环境、换个思路的事。ROCm 在 6.x 之后确实跨过了能用这道坎。再加上 Instinct 那块大得惊人的显存对长文本、大模型的推理场景来说吸引力是实打实的。如果你也想试试其实不用急着去买卡——AMD 开发者云直接就能开 Instinct 实例这套脚本原样丢上去就能跑。 想动手?先领 200 小时免费云算力别让没有硬件成了你动手的借口。现在AMD AI 开发者计划开放注册注册就送 200 小时免费云算力AMD Instinct™ GPU ROCm™ 全栈环境开箱即用不用自己配机器、装驱动、查冲突。点这里加入计划并领取 200 小时免费云算力: https://s.csdn.cn/ik9E3m把这篇文章里的 Docker 镜像、迁移脚本、vLLM 命令原样搬上去今天就能跑通你的第一个 ROCm 任务。试过之后欢迎回来聊聊你的实测数据