Slurm中的GPU分配:--gres vs --gpus-per-task,mpirun vs srun

10
在Slurm中,分配GPU有两种方式:一种是使用通用的--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)。如果需要更多信息,请告诉我。
3个回答

3
我遇到了一个相关的问题。正在运行。
#SBATCH --nodes=1
#SBATCH --ntasks=4
#SBATCH --gres=gpu:1

此操作将导致进程共享单个GPU。

"PROCID=2: GPU 0: NVIDIA A100-SXM4-40GB (UUID: GPU-715daa1d-db6f-9e69-ab48-190158bd5360)"
"PROCID=1: GPU 0: NVIDIA A100-SXM4-40GB (UUID: GPU-715daa1d-db6f-9e69-ab48-190158bd5360)"
"PROCID=0: GPU 0: NVIDIA A100-SXM4-40GB (UUID: GPU-715daa1d-db6f-9e69-ab48-190158bd5360)"
"PROCID=3: GPU 0: NVIDIA A100-SXM4-40GB (UUID: GPU-715daa1d-db6f-9e69-ab48-190158bd5360)"

This I suppose is correct.

Running with

#SBATCH --nodes=1
#SBATCH --ntasks=4
#SBATCH --gpus-per-task=1

这样做会导致只有最后一个进程能够使用GPU。

"PROCID=2: No devices found."
"PROCID=3: No devices found."
"PROCID=0: No devices found."
"PROCID=1: GPU 0: NVIDIA A100-SXM4-40GB (UUID: GPU-02348a17-a825-300c-0336-48e33d0dadb2)"

注意连续运行中的不同ID

"PROCID=2: No devices found."
"PROCID=1: No devices found."
"PROCID=3: No devices found."
"PROCID=0: GPU 0: NVIDIA A100-SXM4-40GB (UUID: GPU-715daa1d-db6f-9e69-ab48-190158bd5360)"

跑步时

#SBATCH --nodes=1
#SBATCH --ntasks=4
#SBATCH --gres=gpu:4

这将导致每个进程都可以访问所有4个GPU。

"PROCID=3: GPU 0: NVIDIA A100-SXM4-40GB (UUID: GPU-715daa1d-db6f-9e69-ab48-190158bd5360) GPU 1: NVIDIA A100-SXM4-40GB (UUID: GPU-02348a17-a825-300c-0336-48e33d0dadb2) GPU 2: NVIDIA A100-SXM4-40GB (UUID: GPU-fbd9a227-e473-b993-215f-8f39b3574fd0) GPU 3: NVIDIA A100-SXM4-40GB (UUID: GPU-7843f55f-a15b-1d4c-229c-39b5c439bd5e)"
"PROCID=1: GPU 0: NVIDIA A100-SXM4-40GB (UUID: GPU-715daa1d-db6f-9e69-ab48-190158bd5360) GPU 1: NVIDIA A100-SXM4-40GB (UUID: GPU-02348a17-a825-300c-0336-48e33d0dadb2) GPU 2: NVIDIA A100-SXM4-40GB (UUID: GPU-fbd9a227-e473-b993-215f-8f39b3574fd0) GPU 3: NVIDIA A100-SXM4-40GB (UUID: GPU-7843f55f-a15b-1d4c-229c-39b5c439bd5e)"
"PROCID=2: GPU 0: NVIDIA A100-SXM4-40GB (UUID: GPU-715daa1d-db6f-9e69-ab48-190158bd5360) GPU 1: NVIDIA A100-SXM4-40GB (UUID: GPU-02348a17-a825-300c-0336-48e33d0dadb2) GPU 2: NVIDIA A100-SXM4-40GB (UUID: GPU-fbd9a227-e473-b993-215f-8f39b3574fd0) GPU 3: NVIDIA A100-SXM4-40GB (UUID: GPU-7843f55f-a15b-1d4c-229c-39b5c439bd5e)"
"PROCID=0: GPU 0: NVIDIA A100-SXM4-40GB (UUID: GPU-715daa1d-db6f-9e69-ab48-190158bd5360) GPU 1: NVIDIA A100-SXM4-40GB (UUID: GPU-02348a17-a825-300c-0336-48e33d0dadb2) GPU 2: NVIDIA A100-SXM4-40GB (UUID: GPU-fbd9a227-e473-b993-215f-8f39b3574fd0) GPU 3: NVIDIA A100-SXM4-40GB (UUID: GPU-7843f55f-a15b-1d4c-229c-39b5c439bd5e)"

运行中

#SBATCH --ntasks=4
#SBATCH --gres=gpu:4
#SBATCH --gpu-bind=single:1

再次执行将导致仅最后一个进程接收GPU。

"PROCID=1: No devices found."
"PROCID=0: No devices found."
"PROCID=3: No devices found."
"PROCID=2: GPU 0: NVIDIA A100-SXM4-40GB (UUID: GPU-fbd9a227-e473-b993-215f-8f39b3574fd0)"

这并没有回答问题。一旦您具有足够的声望,您就可以评论任何帖子;相反,提供不需要询问者澄清的答案。- [来自评审] (/ review / late-answers / 30980568) - SavPhill
1
我理解,但这不是我的答案意图。我发布了相同的问题,但带有更多的解释,希望有人能根据综合信息给出答案。我也在寻找答案。 - Iztok Lebar Bajec
1
测试结果对我很有帮助。谢谢! - transang
你的回答很有见地,对我和许多人来说已经足够了。感谢你抽出时间回答这个帖子并为其做出贡献。 - undefined

3
嗯,FWIW - 变体B1不起作用,因为mpirun在内部只使用srun来启动它的守护进程。只有一个守护进程/节点,因此srun只分配一个GPU给该任务(守护进程)。然后,守护进程fork/exec应用程序进程,这些进程继承了该GPU分配的环境变量。
变体A1之所以起作用,是因为您要求每个任务使用两个GPU,并且恰好在每个节点上运行了两个应用程序进程。如果您运行第三个进程,或者只要求每个任务使用一个GPU,它将失败 - 原因与上述相同。

谢谢,这基本上解释了为什么在多节点情况下直接使用mpirun不起作用。您对为什么变体B2不起作用有什么见解吗? - undefined
请点击此处查看详细说明:https://github.com/open-mpi/ompi/issues/11949 - undefined

2
刚刚遇到了这个问题(或者至少类似的问题)。问题是使用Slurm启动器(srun)与GPU绑定(通过--gpu-bind显式绑定,或通过--gpus-per-task隐式绑定)会阻止CUDA IPC的工作(这是MPI库用于直接GPU-GPU通信的主要机制)。
请注意,CUDA_VISIBLE_DEVICES可能会引起误解:GPU分配也可以通过cgroups进行控制(检查/etc/slurm/cgroup.conf中的ConstrainDevices条目)。检查使用的设备的最佳方法是调用nvidia-smi -L,它会打印设备的UUID。
因此,您有以下选择:
1. 使用不进行绑定的其他启动器(mpiexec或mpirun:请注意,在某些情况下,这可能只是对srun的包装)。 2. 禁用绑定(srun --gpu-bind=none),并手动分配您的设备。

更多信息请参见https://github.com/open-mpi/ompi/issues/11949#issuecomment-1737712291

显然,这是一个OpenMPI的错误消息。我找到了一个关于这个错误的旧线程,建议使用--mca btl_smcuda_use_cuda_ipc 0来禁用CUDA IPC。然而,由于在这种情况下使用了srun来启动程序,我不知道如何将这些参数传递给OpenMPI。

你可能不想这样做,因为它会回退到通过主机进行复制,但你可以通过环境变量传递mca选项,例如:

export OMPI_MCA_btl_smcuda_use_cuda_ipc=0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接