Slurm下--gres/--gpus-per-task与mpirun/srun组合的GPU及MPI异常问题
异常根因拆解
B1(--gpus-per-task+mpirun的GPU映射错误)根因
- Slurm 20.11版本的细粒度GPU资源(
--gpus-per-task)映射逻辑,仅在由Slurm原生srun作为任务启动入口时,才会逐节点、逐任务正确生成裁剪后的CUDA_VISIBLE_DEVICES环境变量并下发到对应进程。 - 当前使用的OpenMPI 4.0.5虽然编译了
--with-slurm适配,但该版本的OpenMPI资源采集组件(RAS)存在跨节点环境读取缺陷:调用mpirun启动多节点任务时,仅会从作业启动节点的全局作业环境中读取GPU配置,不会跨节点拉取每个Slurm step节点的本地资源映射,因此会把启动节点上按单任务1卡裁剪得到的CUDA_VISIBLE_DEVICES=0直接下发到所有节点的进程,导致第二节点所有任务绑定到错误的GPUID。
B2(--gpus-per-task+srun的cuIpcOpenMemHandle报错)根因
- 报错返回值217对应
CUDA_ERROR_IPC_MEM_HANDLE_INVALID,本质不是显存不足,而是OpenMPI的共享内存CUDA通信模块(smcuda)默认尝试跨进程传递CUDA IPC内存句柄时,触发了跨物理GPU的无效操作。 - 该配置下同一节点的不同MPI进程被绑定到不同物理GPU,而CUDA IPC仅支持单块物理GPU内的进程间内存句柄传递,跨物理GPU的调用会直接返回无效错误。之前采用
--gres=gpu:2配置时所有同节点进程的CUDA_VISIBLE_DEVICES为完整列表0,1,模块的IPC逻辑不会触发跨卡的无效调用,因此没有报错。 - smcuda模块在IPC调用失败后会自动回退到普通共享内存通信路径,因此任务不会真正崩溃退出,只会持续刷错误日志。
- 关于MCA参数传递的疑问有明确解法:编译了Slurm适配的OpenMPI 4.0.5会将
srun作为自身的任务启动器,所有OpenMPI运行时参数(MCA)完全可以通过OMPI_MCA_前缀的环境变量传递,不需要在srun命令行显式挂载参数。
落地解决方案
以下方案均支持--gpus-per-task的灵活配置,后续修改--ntasks-per-node时无需手动调整GPU分配数量,Slurm会自动按任务数计算所需GPU资源。
方案1:srun启动(稳定性最优)
- 替换批处理脚本中的GPU配置段:
#SBATCH --gpus-per-task=1 #SBATCH --gpu-bind=single:1 - 在程序启动前添加OpenMPI运行时配置,关闭CUDA IPC避免跨卡报错:
该配置下Slurm会逐任务正确绑定独立GPU,OpenMPI会自动识别环境变量中的MCA参数,完全不会触发无效的跨卡IPC调用。因集群为10G以太网无高速互联,关闭CUDA IPC对整体通信性能无明显负面影响。export OMPI_MCA_btl_smcuda_use_cuda_ipc=0 srun ./sim
方案2:mpirun启动(兼容原有使用习惯)
- 保留
--gpus-per-task=1的GPU配置,关闭srun相关启动段 - 启动前添加三类环境变量,修复跨节点GPU映射读取问题、禁用CUDA IPC报错:
该方案依赖OpenMPI的Slurm适配组件行为,稳定性略低于srun启动方案,若仍出现GPU映射错误,可回退至方案1。# 强制OpenMPI从Slurm资源管理器读取资源配置 export OMPI_MCA_ras=slurm export OMPI_MCA_btl_smcuda_use_cuda_ipc=0 # 逐节点拉取本地Slurm环境变量 export OMPI_MCA_orte_env_purge=0 mpirun ./sim
额外注意事项
- 即使不配置
--gpu-bind=single:1,只要采用--gpus-per-task配置将CUDA_VISIBLE_DEVICES裁剪为单卡,OpenMPI的smcuda模块就会默认启用CUDA IPC,必须添加OMPI_MCA_btl_smcuda_use_cuda_ipc=0环境变量才能避免无效报错刷屏。 - 若后续升级OpenMPI到4.1+版本,可修复跨节点GPU映射读取的兼容问题,
mpirun启动下的映射错误会自行消失,不需要额外调整环境变量。
内容的提问来源于stack exchange,提问作者Jakub Klinkovský
相关产品推荐
相关产品推荐

