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