背景:
我编写了一个 CUDA 程序,该程序对一系列符号执行处理。该程序并行处理所有符号序列,并规定所有序列具有相同的长度。我将数据分类为组,每个组完全由相同长度的序列组成。程序一次处理 1 组。
问题:
我在具有 4 个 GPU 的 Linux 机器上运行我的代码,并希望通过运行我的程序的 4 个实例(每个 GPU 1 个)来利用所有 4 个 GPU。是否可以让程序选择一个未被另一个 CUDA 应用程序使用的 GPU 来运行?我不想硬编码任何会导致问题的东西,当程序在具有更多或更少 GPU 数量的不同硬件上运行时。
环境变量CUDA_VISIBLE_DEVICES
是你的朋友。
我假设您打开的终端数量与GPU数量一样多。 假设您的应用程序名为 myexe
然后在一个终端中,您可以执行以下操作:
CUDA_VISIBLE_DEVICES="0" ./myexe
在下一个终端中:
CUDA_VISIBLE_DEVICES="1" ./myexe
等等。
然后,第一个实例将在 CUDA 枚举的第一个 GPU 上运行。 第二个实例将在第二个 GPU 上运行(仅),依此类推。
假设 bash 和给定的终端会话,您可以通过导出变量来使其"永久":
export CUDA_VISIBLE_DEVICES="2"
此后,在该会话中运行的所有 CUDA 应用程序将仅观察第三个枚举的 GPU(枚举从 0 开始),并且它们将观察该 GPU,就好像它是会话中的设备 0 一样。
这意味着,假设应用使用默认 GPU 或 GPU 0,则无需对此方法的应用程序进行任何更改。
您还可以扩展它以使多个 GPU 可用,例如:
export CUDA_VISIBLE_DEVICES="2,4"
意味着通常枚举为 2 和 4 的 GPU 现在将成为该会话中唯一"可见"的 GPU,它们将枚举为 0 和 1。
在我看来,上述方法是最简单的。 选择"未使用"的 GPU 是有问题的,因为:
- 我们需要一个"使用中"的定义
- 在特定时刻正在使用的 GPU 可能不会在此之后立即使用
- 最重要的是,未"使用中"的 GPU 可能会异步变为"使用中",这意味着您将面临竞争条件。
因此,最好的建议(IMO)是明确管理GPU。 否则,您需要某种形式的作业调度程序(不在此问题的范围之内,IMO)才能查询未使用的 GPU 并在另一个应用程序尝试这样做之前以有序的方式"保留"一个。
有一种更好(更自动)的方法,我们在PIConGPU中使用,它在大型(和不同的)集群上运行。请参阅此处的实现:https://github.com/ComputationalRadiationPhysics/picongpu/blob/909b55ee24a7dcfae8824a22b25c5aef6bd098de/src/libPMacc/include/Environment.hpp#L169
基本上:调用cudaGetDeviceCount
以获取GPU的数量,迭代它们并调用cudaSetDevice
将其设置为当前设备并检查是否有效。由于 CUDA 中的某些错误,此检查可能涉及测试创建流,这些错误使 setDevice 成功,但所有后来的调用都失败,因为设备实际正在使用中。注意:您可能需要将 GPU 设置为独占模式,以便一个 GPU 只能由一个进程使用。如果您没有足够的"批处理"数据,则可能需要相反的情况:多进程将工作提交到一个GPU。因此,请根据您的需要进行调整。
其他想法是:启动一个 MPI 应用程序,每个等级的进程数与 GPU 相同,并使用与本地秩号相同的设备编号。这也有助于像您这样具有不同数据集要分发的应用程序。因此,例如,您可以拥有 MPI 等级 0 进程长度 1 数据和 MPI 等级 1 进程长度 2 数据等。