与第三方CUDA库链接会减慢cudaMalloc的速度。

10

众所周知,在CUDA 4.x上,对cudaMalloc的第一次调用可能会非常慢(已经有人多次报告了),这似乎是CUDA驱动程序中的一个错误。

最近我注意到一些奇怪的行为: cudaMalloc的运行时间直接取决于我将多少第三方CUDA库链接到我的程序中 (请注意,我不使用这些库,只是将我的程序与它们链接)

我使用以下程序进行了一些测试:

int main() {
  cudaSetDevice(0);
  unsigned int *ptr = 0;
  cudaMalloc((void **)&ptr, 2000000 * sizeof(unsigned int));   
  cudaFree(ptr);
return 1;
}

以下是结果:

  • 链接: -lcudart -lnpp -lcufft -lcublas -lcusparse -lcurand 运行时间: 5.852449

  • 链接: -lcudart -lnpp -lcufft -lcublas 运行时间: 1.425120

  • 链接: -lcudart -lnpp -lcufft 运行时间: 0.905424

  • 链接: -lcudart 运行时间: 0.394558

根据 'gdb' ,时间确实用在了 cudaMalloc 上,因此不是由某些库初始化例程引起的。

我想知道是否有人能对此做出合理的解释?

1个回答

11
在你的例子中,cudaMalloc调用会在GPU上启动延迟上下文建立。当运行时API库被包含时,它们的二进制有效负载必须被检查,并且其中包含的GPU ELF符号和对象会合并到上下文中。库越多,您可以预期这个过程需要更长时间。此外,如果cubins中有任何架构不匹配,并且您有向后兼容的GPU,则还可能触发为目标GPU重新编译设备代码的驱动程序重新编译。在一个非常极端的情况下,我曾经看到一个旧版本的CUBLAS链接的旧应用程序在Fermi GPU上运行时需要10秒钟以上才能加载和初始化。
您可以通过像这样发出cudaFree调用来明确强制延迟上下文建立:
int main() {
    cudaSetDevice(0);
    cudaFree(0); // context establishment happens here
    unsigned int *ptr = 0;
    cudaMalloc((void **)&ptr, 2000000 * sizeof(unsigned int));   
    cudaFree(ptr);
  return 1;
}

如果您使用计时器对此版本进行分析或监测,您会发现第一个cudaFree调用会消耗大部分运行时间,而cudaMalloc调用几乎没有任何开销。


感谢@talomnies,确实在开头插入cudaFree会占用整个运行时间。我最初在GT650M显卡(Kepler核心)上测试了这个程序,而在Fermi GPU GTX580上,它甚至需要更长的时间-大约7秒。尽管如此,NVIDIA仍然可以做一些优化他们的上下文管理-7秒的完全CPU工作负载似乎太多了。 - user1545642
@asm:尝试使用CUDA 5并查看其效果。现在工具链中有一个适当的设备代码链接器,因此运行时的一些开销可能会转移到编译和链接时间(或至少流畅一些)。另外,如果您发现这回答了您的问题,您可以很友善地接受它,以便将您的问题标记为已回答。 - talonmies
请注意,在CUDA 4.0中,CUDA初始化所需时间如此之长的部分原因是驱动程序正在执行大量的虚拟内存分配以进行统一虚拟寻址。 - ArchaeaSoftware
还要注意的是,驱动程序会在磁盘上缓存JIT编译的内核。如果更改硬件,则必须重新执行此操作,但就内核编译而言,第二次应该比第一次快得多。 - ArchaeaSoftware

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接