在多 GPU 计算机中,如何指定 CUDA 作业应在哪个 GPU 上运行?
举个例子,在安装 CUDA 时,我选择安装
NVIDIA_CUDA-<#.#>_Samples
,然后运行 nbody
模拟的多个实例,但它们都在一个 GPU 0 上运行; GPU 1 完全空闲(使用 watch -n 1 nvidia-dmi
进行监控)。使用 检查
CUDA_VISIBLE_DEVICES
echo $CUDA_VISIBLE_DEVICES
我发现这个没有设置。我尝试使用
设置它CUDA_VISIBLE_DEVICES=1
然后再次运行
nbody
,但它也转到 GPU 0。
我查看了相关问题,如何选择指定GPU来运行CUDA程序?,但是
deviceQuery
命令不在CUDA 8.0 bin目录中。除了$CUDA_VISIBLE_DEVICES$
之外,我看到其他帖子提到了环境变量$CUDA_DEVICES
,但这些都没有设置,我也没有找到如何使用它的信息。
虽然与我的问题没有直接关系,但使用
nbody -device=1
我能够让应用程序在 GPU 1 上运行,但使用 nbody -numdevices=2
无法在 GPU 0 和 1 上运行。
我正在使用 bash shell、CentOS 6.8、CUDA 8.0、2 个 GTX 1080 GPU 和 NVIDIA 驱动程序 367.44 运行的系统上对此进行测试。
我知道使用 CUDA 进行编写时,您可以管理和控制要使用的 CUDA 资源,但是在运行已编译的 CUDA 可执行文件时,如何从命令行管理它?
该问题是由于未正确设置 shell 中的
CUDA_VISIBLE_DEVICES
变量引起的。
例如,要指定 CUDA 设备
1
,您可以使用 设置
CUDA_VISIBLE_DEVICES
export CUDA_VISIBLE_DEVICES=1
或
CUDA_VISIBLE_DEVICES=1 ./cuda_executable
前者为当前 shell 的生命周期设置变量,后者仅为该特定可执行调用的生命周期设置变量。
如果您想指定多个设备,请使用
export CUDA_VISIBLE_DEVICES=0,1
或
CUDA_VISIBLE_DEVICES=0,1 ./cuda_executable
如果其他人在 Python 中执行此操作并且无法正常工作,请尝试在之前进行 pycuda 和 tensorflow 的导入。
即:
import os
os.environ["CUDA_DEVICE_ORDER"] = "PCI_BUS_ID"
os.environ["CUDA_VISIBLE_DEVICES"] = "0"
...
import pycuda.autoinit
import tensorflow as tf
...
如此处所示。
您还可以在命令行中设置 GPU,这样您就不需要将设备硬编码到脚本中(这在没有多个 GPU 的系统上可能会失败)。假设您想在 5 号 GPU 上运行脚本,您可以在命令行中键入以下内容,它将在 GPU#5 上运行您的脚本一次:
CUDA_VISIBLE_DEVICES=5, python test_script.py
设置以下两个环境变量:
NVIDIA_VISIBLE_DEVICES=$gpu_id
CUDA_VISIBLE_DEVICES=0
其中
gpu_id
是您所选 GPU 的 ID,如主机系统的 nvidia-smi
(从 0 开始的整数)中所示,它将可供来宾系统(例如 Docker 容器环境)使用。
您可以通过检查在来宾系统终端中运行的
Bus-Id
中的nvidia-smi
参数来验证是否为每个gpu_id值选择了不同的卡。
这种基于
NVIDIA_VISIBLE_DEVICES
的方法只向系统公开一张卡(本地 ID 为零),因此我们还将另一个变量 CUDA_VISIBLE_DEVICES
硬编码为 0(主要是为了防止它默认为空字符串,表示没有 GPU)。
请注意,环境变量应在客户系统启动之前设置(因此无法在 Jupyter Notebook 的终端中进行设置),例如在 Kubernetes 或 Openshift 中使用
docker run -e NVIDIA_VISIBLE_DEVICES=0
或 env
。
如果您想要 GPU 负载平衡,请在每个来宾系统启动时使
gpu_id
随机。
如果使用 python 设置此值,请确保对所有环境变量(包括数字变量)使用字符串。
您可以通过检查
gpu_id
的 Bus-Id 参数(在来宾系统中运行的终端中)来验证是否为 nvidia-smi
的每个值选择了不同的卡。
仅基于
CUDA_VISIBLE_DEVICES
的公认解决方案不会隐藏其他卡(与固定卡不同),因此如果您尝试在支持 GPU 的 python 包中使用它们,则会导致访问错误。通过此解决方案,其他卡对访客系统不可见,但其他用户仍然可以访问它们并在平等的基础上共享其计算能力,就像 CPU 一样(已验证)。
这也比使用 Kubernetes / Openshift 控制器 (
resources.limits.nvidia.com/gpu
) 的解决方案更可取,后者会对分配的卡施加锁定,将其从可用资源池中删除(因此具有 GPU 访问权限的容器数量不能超过实体卡)。
这已在运行 Ubuntu 18.04 或 20.04 的 docker 容器中的 CUDA 8.0、9.0、10.1 和 11.2 下进行了测试,并由 Openshift 3.11 编排。
下面的评论中有一个由 lukaszzenko 修改的解决方案,它使用相同的想法并产生相同的输出。考虑使用它,因为它更简洁:
export CUDA_VISIBLE_DEVICES=$(nvidia-smi --query-gpu=memory.free,index --format=csv,nounits,noheader | sort -nr | head -1 | awk '{ print $NF }')
在路径中提供 xml2json 后,您可以选择利用率最低的 N 个 GPU:
export CUDA_VISIBLE_DEVICES=$(nvidia-smi -x -q | xml2json | jq '.' | python -c 'import json;import sys;print(",".join([str(gpu[0]) for gpu in sorted([(int(gpu["minor_number"]), float(gpu["utilization"]["gpu_util"].split(" ")[0])) for gpu in json.load(sys.stdin)["nvidia_smi_log"]["gpu"]], key=lambda x: x[1])[:2]]))')
如果您需要单个 GPU 或根据可用 GPU 的最大数量的任何数量,只需将
[:2]
替换为 [:1]
。
对于随机 GPU,你可以这样做:
export CUDA_VISIBLE_DEVICES=$((( RANDOM % 8 )))