【问题标题】:How do I select which GPU to run a job on?如何选择在哪个 GPU 上运行作业?
【发布时间】:2017-01-31 15:00:28
【问题描述】:

在多 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。

我查看了相关问题,how to choose designated GPU to run CUDA program?,但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 可执行文件时,我该如何从命令行进行管理?

【问题讨论】:

  • nbody 应用程序有一个命令行选项来选择要在其上运行的 GPU - 您可能想研究该代码。对于更一般的情况,CUDA_VISIBLE_DEVICES 应该可以工作。如果没有,您可能没有正确使用它,您可能应该给出一个完整示例来说明您尝试过的操作。您还应该指出您正在使用的操作系统以及用于 linux 的操作系统、shell(例如 bash、csh 等)。 deviceQuery 对这些都不是必需的,它只是一个示例应用程序来演示 CUDA_VISIBLE_DEVICES 的行为。正确的环境变量名称中没有 $
  • 您需要了解有关您正在使用的 bash shell 的更多信息。这:CUDA_VISIBLE_DEVICES=1 不会永久设置环境变量(事实上,如果这就是你在命令行上的全部内容,它真的没有任何用处。)。这:export CUDA_VISIBLE_DEVICES=1 将在该会话的其余部分永久设置它。您可能想研究环境变量在 bash 中是如何工作的,以及各种命令如何影响它们,以及持续多长时间。
  • deviceQuery 随 CUDA 8 提供,但您必须构建它。如果您阅读 CUDA 8 linux 安装指南,它将解释如何构建 deviceQuery
  • 在 /usr/local/cuda/bin 中有一个 cuda-install-samples-.sh 脚本,如果未安装示例,您可以使用该脚本。然后,在 NVIDIA_Samples 安装目录的 1_Utilities 文件夹中,您将找到 deviceQuery。只需在该文件夹中调用 make 即可为您编译它。如果我没记错的话,它会将二进制文件复制到同一个文件夹中。
  • 应该是watch -n 1 nvidia-smi...

标签: cuda nvidia


【解决方案1】:

问题是由于没有在 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

【讨论】:

  • 如果CUDA_VISIBLE_DEVICE=0会发生什么?
  • @KurianBenoy 设置 CUDA_VISIBLE_DEVICE=0 将选择 GPU 0 来执行任何 CUDA 任务。我认为这是默认行为,因为在我设置变量之前我的所有 GPU 任务都将转到 GPU 0,因此根据您的用例,可能没有必要实际设置它。
  • @StevenC.Howell 我在想 CUDA_VISIBLE_DEVICE=0 表示 CPU 系统。感谢您的澄清
  • @KurianBenoy CUDA_VISIBLE_DEVICES="" 表示 CPU
  • 如果我在源代码中使用 cudaSetDevice 时设置 CUDA_VISIBLE_DEVICE=0 会怎样?
【解决方案2】:

如果其他人在 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
...

here所见。

【讨论】:

  • 这很好用!我在终端中使用它而不是 python export CUDA_DEVICE_ORDER=PCI_BUS_ID 然后 export CUDA_VISIBLE_DEVICES=<GPU_NUMBER>
【解决方案3】:

设置以下两个环境变量:

NVIDIA_VISIBLE_DEVICES=$gpu_id
CUDA_VISIBLE_DEVICES=0

gpu_id 是您选择的 GPU 的 ID,如主机系统的 nvidia-smi(一个从 0 开始的整数)中所示,它将提供给来宾系统(例如 Docker 容器环境)。

您可以通过检查nvidia-smi 在来宾系统的终端中运行的Bus-Id 参数来验证是否为每个 gpu_id 值选择了不同的卡。

更多信息

这种基于NVIDIA_VISIBLE_DEVICES的方法只向系统公开了一张卡(本地ID为零),因此我们还将另一个变量CUDA_VISIBLE_DEVICES硬编码为0(主要是为了防止它默认为空表示没有 GPU 的字符串)。

请注意,环境变量应在启动来宾系统之前设置(因此不可能在 Jupyter Notebook 的终端中进行设置),例如在 Kubernetes 或 Openshift 中使用 docker run -e NVIDIA_VISIBLE_DEVICES=0env

如果您想要 GPU 负载平衡,请在每次来宾系统启动时随机设置 gpu_id

如果使用 python 进行设置,请确保所有环境变量(包括数字变量)都是 using strings

您可以通过检查 nvidia-smi 的 Bus-Id 参数(在来宾系统中运行的终端中)验证是否为 gpu_id 的每个值选择了不同的卡。

仅基于CUDA_VISIBLE_DEVICES 的公认解决方案不会隐藏其他卡(与固定卡不同),因此如果您尝试在启用 GPU 的 python 包中使用它们会导致访问错误。使用此解决方案,访客系统看不到其他卡,但其他用户仍然可以访问它们并在平等的基础上分享他们的计算能力,就像 CPU 一样(已验证)。

这也比使用 Kubernetes / Openshift 控制器 (resources.limits.nvidia.com/gpu) 的解决方案更可取,后者将对分配的卡施加锁定,将其从可用资源池中删除(因此具有 GPU 访问权限的容器数量不能超过实体卡的数量)。

这已在 CUDA 8.0、9.0、10.1 和 11.2 下在运行 Ubuntu 18.04 或 20.04 并由 Openshift 3.11 编排的 docker 容器中进行了测试。

【讨论】:

    【解决方案4】:

    您还可以在命令行中设置 GPU,这样您就无需将设备硬编码到您的脚本中(这在没有多个 GPU 的系统上可能会失败)。假设您想在 GPU 5 上运行脚本,您可以在命令行中键入以下内容,它会在 GPU #5 上运行您的脚本一次:

    CUDA_VISIBLE_DEVICES=5, python test_script.py
    

    【讨论】:

      【解决方案5】:

      对于随机 gpu,您可以这样做:

      export CUDA_VISIBLE_DEVICES=$((( RANDOM % 8 )))
      

      【讨论】:

        【解决方案6】:

        选择利用率最低的 GPU

        在您的路径中设置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]

        【讨论】:

        • 更简单的方法是export CUDA_VISIBLE_DEVICES=$(nvidia-smi --query-gpu=memory.free,index --format=csv,nounits,noheader | sort -nr | head -1 | awk '{ print $NF }')
        猜你喜欢
        • 1970-01-01
        • 2017-03-21
        • 1970-01-01
        • 2020-11-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多