我有一个应用程序,可以在用户系统上的 GPU 之间分配处理负载。基本上,每个 GPU 都有一个 CPU 线程来启动一个GPU处理间隔当由主应用程序线程定期触发时。
考虑以下图像(使用 NVIDIA 的 CUDA 分析器工具生成)作为示例GPU处理间隔-- 这里应用程序使用单个 GPU。
如您所见,两个排序操作消耗了很大一部分 GPU 处理时间,我为此使用 Thrust 库 (thrust::sort_by_key)。另外,看起来 Thrust::sort_by_key 在开始实际排序之前会在后台调用一些 cudaMalloc。
现在考虑相同的处理间隔,其中应用程序将处理负载分散到两个 GPU 上:
在理想情况下,您会期望 2 个 GPU 处理间隔恰好是单个 GPU 处理间隔的一半(因为每个 GPU 执行一半的工作)。正如您所看到的,情况并非如此,部分原因是由于某种争用问题,同时调用 cudaMalloc 时它们似乎需要更长的时间(有时长 2-3 倍)。我不明白为什么需要这样,因为 2 个 GPU 的内存分配空间是完全独立的,所以 cudaMalloc 上不应该有系统范围的锁——每个 GPU 的锁会更合理。
为了证明我的假设(问题在于同时调用 cudaMalloc),我创建了一个极其简单的程序,其中有两个 CPU 线程(针对每个 GPU),每个线程调用 cudaMalloc 多次。我首先运行这个程序,以便单独的线程不会同时调用 cudaMalloc:
您会看到每次分配大约需要 175 微秒。接下来,我使用同时调用 cudaMalloc 的线程运行该程序:
在这里,每个调用花费了大约 538 微秒,或者说比之前的情况长了 3 倍!不用说,这极大地减慢了我的应用程序的速度,而且按理说,如果 GPU 数量超过 2 个,问题只会变得更糟。
我在 Linux 和 Windows 上注意到了这种行为。在 Linux 上,我使用 Nvidia 驱动程序版本 319.60,在 Windows 上我使用 327.23 版本。我正在使用 CUDA 工具包 5.5。
可能的原因:我在这些测试中使用的是 GTX 690。该卡基本上是将 2 680 个类似 GPU 安置在同一单元中。这是我运行过的唯一“多 GPU”设置,所以也许 cudaMalloc 问题与 690 的 2 个 GPU 之间的某些硬件依赖性有关?