背景:
我编写了一个CUDA程序,用于处理一系列符号。该程序并行处理所有符号序列,但要求所有序列的长度相同。我将数据分类为具有相同长度的序列组。程序每次处理1个组。
问题:
我在拥有4个GPU的Linux机器上运行代码,并希望通过运行4个程序实例(每个GPU运行1个)来利用所有4个GPU。是否可能让程序选择一个未被另一个CUDA应用程序使用的GPU来运行?我不想硬编码任何内容,以免在将程序运行在具有更多或更少GPU的不同硬件上时出现问题。
背景:
我编写了一个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并在有序方式中“预留”一个,然后再另一个应用程序尝试这样做之前。
CUDA_VISIBLE_DEVICES
选择了程序要运行的GPU,因此似乎这个环境变量对于OpenCL也有效(在NVIDIA GPU上),但我不知道是否有任何规定。这只是我的观察。 - Robert Crovella有一种更好(更自动化)的方法,我们在运行于巨大(且不同)集群上的PIConGPU中使用。 请在此处查看实现:https://github.com/ComputationalRadiationPhysics/picongpu/blob/909b55ee24a7dcfae8824a22b25c5aef6bd098de/src/libPMacc/include/Environment.hpp#L169
基本上:调用cudaGetDeviceCount
以获取GPU数量,对它们进行迭代,并调用cudaSetDevice
将其设置为当前设备并检查是否有效。这个检查可能涉及到由于CUDA中的某些错误而需要测试创建流,这使得setDevice成功,但所有后续调用都失败了,因为设备实际上正在使用中。
注意:您可能需要将GPU设置为独占模式,以便一个GPU只能被一个进程使用。如果您没有足够的一批数据,您可能希望相反:多个进程向一个GPU提交工作。因此,请根据您的需求进行调整。