--gres=gpu:N
参数,另一种是使用像--gpus-per-task=N
这样的特定参数。在批处理脚本中启动MPI任务也有两种方式:一种是使用srun
,另一种是使用通常的mpirun
(当OpenMPI编译支持Slurm时)。我发现这些方法之间的行为差异有些出乎意料。我正在使用
sbatch
提交一个批处理作业,基本脚本如下:#!/bin/bash
#SBATCH --job-name=sim_1 # job name (default is the name of this file)
#SBATCH --output=log.%x.job_%j # file name for stdout/stderr (%x will be replaced with the job name, %j with the jobid)
#SBATCH --time=1:00:00 # maximum wall time allocated for the job (D-H:MM:SS)
#SBATCH --partition=gpXY # put the job into the gpu partition
#SBATCH --exclusive # request exclusive allocation of resources
#SBATCH --mem=20G # RAM per node
#SBATCH --threads-per-core=1 # do not use hyperthreads (i.e. CPUs = physical cores below)
#SBATCH --cpus-per-task=4 # number of CPUs per process
## nodes allocation
#SBATCH --nodes=2 # number of nodes
#SBATCH --ntasks-per-node=2 # MPI processes per node
## GPU allocation - variant A
#SBATCH --gres=gpu:2 # number of GPUs per node (gres=gpu:N)
## GPU allocation - variant B
## #SBATCH --gpus-per-task=1 # number of GPUs per process
## #SBATCH --gpu-bind=single:1 # bind each process to its own GPU (single:<tasks_per_gpu>)
# start the job in the directory it was submitted from
cd "$SLURM_SUBMIT_DIR"
# program execution - variant 1
mpirun ./sim
# program execution - variant 2
#srun ./sim
在第一个块中,#SBATCH
选项非常明显和无聊。接下来,我描述的行为在作业在至少2个节点上运行时是可观察到的。因为每个节点有2个GPU,所以我每个节点运行2个任务。
最后,有两种GPU分配(A和B)和两种程序执行(1和2)。因此,总共有4种变体:A1,A2,B1,B2。
变体A1(--gres=gpu:2, mpirun)
变体A2(--gres=gpu:2, srun)
在变体A1和A2中,作业以最佳性能正确运行,我们在日志中获得以下输出:
Rank 0: rank on node is 0, using GPU id 0 of 2, CUDA_VISIBLE_DEVICES=0,1
Rank 1: rank on node is 1, using GPU id 1 of 2, CUDA_VISIBLE_DEVICES=0,1
Rank 2: rank on node is 0, using GPU id 0 of 2, CUDA_VISIBLE_DEVICES=0,1
Rank 3: rank on node is 1, using GPU id 1 of 2, CUDA_VISIBLE_DEVICES=0,1
变量B1 (--gpus-per-task=1,mpirun)
作业未能正确执行,由于第二个节点上的 CUDA_VISIBLE_DEVICES=0
,GPU未被正确映射:
Rank 0: rank on node is 0, using GPU id 0 of 2, CUDA_VISIBLE_DEVICES=0,1
Rank 1: rank on node is 1, using GPU id 1 of 2, CUDA_VISIBLE_DEVICES=0,1
Rank 2: rank on node is 0, using GPU id 0 of 1, CUDA_VISIBLE_DEVICES=0
Rank 3: rank on node is 1, using GPU id 0 of 1, CUDA_VISIBLE_DEVICES=0
注意,这个变体无论是否使用 --gpu-bind=single:1
, 行为是一致的。
B2 变体(--gpus-per-task=1, --gpu-bind=single:1, srun)
GPU 被正确映射(现在每个进程只能看到一个 GPU,因为使用了 --gpu-bind=single:1
):
Rank 0: rank on node is 0, using GPU id 0 of 1, CUDA_VISIBLE_DEVICES=0
Rank 1: rank on node is 1, using GPU id 0 of 1, CUDA_VISIBLE_DEVICES=1
Rank 2: rank on node is 0, using GPU id 0 of 1, CUDA_VISIBLE_DEVICES=0
Rank 3: rank on node is 1, using GPU id 0 of 1, CUDA_VISIBLE_DEVICES=1
然而,在进程开始通信时出现了MPI错误(类似的消息会为每个进程重复一次):
--------------------------------------------------------------------------
The call to cuIpcOpenMemHandle failed. This is an unrecoverable error
and will cause the program to abort.
Hostname: gp11
cuIpcOpenMemHandle return value: 217
address: 0x7f40ee000000
Check the cuda.h file for what the return value means. A possible cause
for this is not enough free device memory. Try to reduce the device
memory footprint of your application.
--------------------------------------------------------------------------
虽然提示“这是一个不可恢复的错误”,但实际上执行过程似乎仍在正常进行,只是日志中会有大量类似于以下消息(假设每次MPI通信调用都会产生一条消息):
[gp11:122211] Failed to register remote memory, rc=-1
[gp11:122212] Failed to register remote memory, rc=-1
[gp12:62725] Failed to register remote memory, rc=-1
[gp12:62724] Failed to register remote memory, rc=-1
显然这是一个OpenMPI错误消息。我在这个错误的旧线程中找到建议,建议使用
--mca btl_smcuda_use_cuda_ipc 0
禁用CUDA IPC。但是,在这种情况下,使用srun
启动程序,我不知道如何将这些参数传递给OpenMPI。请注意,在这种变体中,
--gpu-bind=single:1
仅影响可见GPU(CUDA_VISIBLE_DEVICES
)。但即使没有此选项,每个任务仍然能够选择正确的GPU,并且错误仍然会出现。有没有任何想法,如何解决B1和B2变体中的错误?理想情况下,我们想使用
--gpus-per-task
,它比--gres=gpu:...
更灵活(当我们更改--ntasks-per-node
时,少了一个参数)。对我们来说,使用mpirun与srun无关紧要。我们有Slurm 20.11.5.1,OpenMPI 4.0.5(使用
--with-cuda
和--with-slurm
构建),以及CUDA 11.2.2。操作系统是Arch Linux。网络是10G以太网(没有InfiniBand或OmniPath)。如果需要更多信息,请告诉我。