从 hipify-perl 到手动“填坑”:CUDA 迁移 ROCm 的实战记录

最近手头有个项目需要从 NVIDIA 平台迁移到 AMD GPU,起初想着有官方工具链应该能“一键搞定”,结果跑完自动化脚本才发现,真正的硬仗才刚刚开始。对于熟悉 CUDA 的开发者来说,HIPify 确实是个神器,它能帮你完成 90% 的机械性工作,但剩下那 10% 的“手工活”往往决定了迁移的成败。今天就来复盘一下从运行 hipify-perl 到解决编译报错、手动替换算子的真实过程,希望能帮正在踩坑的你省点时间。

自动化转换的局限与初步排查

迁移的第一步自然是使用 hipify-perlhipify-clang 对源码目录进行扫描。执行命令很简单:

hipify-perl project_src/ -o project_hip/

大多数情况下,像 cudaMalloccudaMemcpy 这样的标准 API 会被准确映射为 hipMallochipMemcpy,内核启动语法 <<< >>> 也能自动转换。如果你运气好,代码里全是标准算子,那恭喜,编译可能一次就过。

但现实往往没那么理想。在我的项目中,跑完脚本后直接编译,立刻抛出了一堆链接错误。仔细检查生成的代码发现,工具虽然替换了大部分关键字,但在涉及第三方库调用时却“装傻”了。特别是原本依赖 cuBLAS 的部分,工具并没有自动将其转换为 rocBLAS,而是保留了原有的命名空间,导致链接器找不到符号。此外,还有一些针对特定 CUDA 版本的新特性,工具直接跳过未处理,留下了待修复的标记。这时候就不能指望工具了,必须人工介入,逐行审查那些报错的模块。

手动替换 cuBLAS 为 rocBLAS 的实操细节

最典型的“硬骨头”就是线性代数库的迁移。在 CUDA 代码中,我们习惯这样调用矩阵乘法:

#include <cublas_v2.h>
// ...
cublasHandle_t handle;
cublasCreate(&handle);
cublasSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N, m, n, k, &alpha, d_A, lda, d_B, ldb, &beta, d_C, ldc);

这段代码在 HIP 环境下是行不通的,因为 cublas_v2.hcublasSgemm 都是 NVIDIA 特有的。我们需要手动将其修改为 ROCm 对应的 rocBLAS 接口。修改后的代码如下:

#include <rocblas/rocblas.h>
// ...
rocblas_handle handle;
rocblas_create_handle(&handle);
rocblas_sgemm(handle, rocblas_operation_none, rocblas_operation_none, 
              m, n, k, &alpha, d_A, lda, d_B, ldb, &beta, d_C, ldc);

注意这里不仅仅是头文件和函数名的变化,枚举值也从 CUBLAS_OP_N 变成了 rocblas_operation_none。如果你的项目中大量使用了 cuBLAS 的高级特性(如 Batched GEMM 或 Tensor Core 操作),这部分工作量会比较大。建议先写一个最小的可复现 Demo,调通后再批量替换主工程代码,避免陷入复杂的依赖泥潭。

除了 cuBLAS,还有 cuDNN 需要对应替换为 MIOpen。这部分的 API 差异更大,有时甚至需要重构整个推理流程,不能简单地进行字符串替换。

搞定环境变量与头文件路径陷阱

代码改完了,下一步就是编译。这时候最容易遇到的问题是编译器找不到头文件,或者链接到了错误的库。这是因为很多构建系统(如 CMake 或 Makefile)默认会去搜索 CUDA 的安装路径。

在 AMD 环境下,必须显式指定 ROCm 的路径。我通常在编译前导出以下环境变量:

export ROCM_PATH=/opt/rocm
export HIP_VISIBLE_DEVICES=0
export CXX=/opt/rocm/bin/hipcc

如果是使用 CMake 的项目,还需要在 CMakeLists.txt 中强制指定包含目录和链接库路径,防止它自动探测到系统中残留的 CUDA 包:

include_directories(${ROCM_PATH}/include)
link_directories(${ROCM_PATH}/lib)
# 显式链接 rocblas 而不是 cublas
target_link_libraries(my_app roc::rocblas)

有一次我遇到一个诡异的报错,提示 undefined reference to 'hipLaunchKernel'。排查半天发现,是因为构建脚本里硬编码了 -lcudart,而我没有及时发现。这种隐式的 CUDA 依赖是最难搞的,建议在迁移初期就用全局搜索把项目里所有的 cudacublascurand 等关键词筛一遍,确保没有漏网之鱼。

结语

从 CUDA 到 ROCm 的迁移,本质上是一场与细节的博弈。hipify 帮我们扫清了障碍,但最后那一公里的路还得靠自己走。无论是手动替换算子库,还是调试复杂的环境变量,每一步都需要对底层原理有清晰的认识。好在随着 ROCm 生态的成熟,越来越多的坑已经被填平,社区的支持也在变好。当你看到代码在 AMD 显卡上顺利跑通,且性能达标时,你会发现这些折腾都是值得的。

200小时GPU算力已就位,快来领取:https://marketing.csdn.net/questions/Q2604140858304426315?utm_source=AIpaper
在这里插入图片描述

Logo

免费领 150 小时云算力,进群参与显卡、AI PC 幸运抽奖

更多推荐