【问题标题】:Correct place to use cudaSetDeviceFlags?使用 cudaSetDeviceFlags 的正确位置?
【发布时间】:2017-11-08 09:28:07
【问题描述】:

Win10 x64、CUDA 8.0、VS2015、6 核 CPU(12 个逻辑核心)、2 个 GTX580 GPU。

一般来说,我正在开发一个多线程应用程序,它启动与 2 个可用 GPU 相关联的 2 个线程,这些线程存储在线程池中。

每个线程在启动时都会执行以下初始化过程(即,这仅在每个线程的运行时执行):

::cudaSetDevice(0 or 1, as we have only two GPUs);
::cudaDeviceSetCacheConfig(cudaFuncCachePreferL1);
::cudaSetDeviceFlags(cudaDeviceMapHost | cudaDeviceScheduleBlockingSync);

然后,从其他工作线程(另外 12 个完全不接触 GPU 的线程)开始,我开始为这 2 个与 GPU 相关的工作线程提供数据,只要启动的 GPU 线程数量相等,它就可以完美运行到可用物理 GPU 的数量。

现在我想启动 4 个 GPU 线程(即每个 GPU 2 个线程)并让每个线程通过单独的 CUDA 流工作。我知道正确使用 CUDA 流所必需的要求,我满足所有这些要求。我失败的是上面提到的初始化过程。

一旦尝试从不同的 GPU 线程但对于同一个 GPU 执行此过程两次, ::cudaSetDeviceFlags(...) 就会开始失败,并显示 “当设备在此进程中处于活动状态时无法设置” 错误消息。

我查看了手册,似乎明白了发生这种情况的原因,但我不明白如何正确使用 ::cudaSetDeviceFlags(...) 进行设置。

我可以评论这条 ::cudaSetDeviceFlags(...) 行,即使每个 GPU 有 8 个线程,propgram 也能正常工作,但我需要设置 cudaDeviceMapHost 标志才能使用流,固定内存不会否则可用。

编辑要考虑的额外信息 #1:

  1. 如果在 ::cudaSetDevice 之前调用 ::cudaSetDeviceFlags 则不会出错 发生。
  2. 每个 GPU 线程通过以下方式分配一块固定内存 ::VirtualAlloc ->::cudaHostRegister 线程启动时的方法 (无论启动多少 GPU 线程都可以正常工作)和 在线程终止时释放它(通过 ::cudaHostUnregister -> ::虚拟免费)。 ::cudaHostUnregister 失败并显示“指针不 如果每个 GPU 的线程数大于 1,则对应于一半线程的已注册内存区域。

【问题讨论】:

  • 你试过cudaDeviceReset吗?您可以在设置标志之前按线程初始化调用它。但是你需要检查设备是否被其他线程使用,因为这个函数会立即重置设备。
  • 明确地捕获并忽略错误不是最简单的事情吗?这意味着另一个线程已经按照您想要的方式设置了 GPU。我有 99% 的把握这个错误是良性的,只是你没有提供一个复制案例,我不会自己写一个。试试看
  • @Hamed:试过了,没有任何变化,同样的错误。
  • @Roman 使用您在评论中提供的这些新信息考虑 EDIT 您的问题,您可能会在那里得到更好的关注。
  • #1 无关紧要,如果您不进行 cudaSetDevice 调用,线程将不会附加到正确的上下文,也不会发生任何事情。 #2也几乎无关紧要。 GPU 上的上下文由生成它的线程拥有。如果该线程退出,则上下文将被销毁。任何其他尝试在同一个(现在是死上下文)上调用 API 的线程都会产生运行时错误。

标签: c++ multithreading cuda


【解决方案1】:

嗯,非常复杂的 trythis-trythat-seewhathappens-tryagain 练习终于奏效了,一如既往。

这是 ::cudaSetDeviceFlags() 文档的摘录:

记录标志作为初始化当前使用的标志 设备。 如果没有设备成为调用线程的当前设备,则 标志将应用于初始化任何设备的初始化 由调用主机线程,除非该设备已经拥有 由这个或任何主机线程显式设置的初始化标志。

因此,在 GPU 工作线程中,需要调用 ::cudaSetDeviceFlags() 之前 ::cudaSetDevice()。

我在 GPU 线程初始化代码中实现了类似的东西,以确保在设备集实际应用之前设置的设备标志被正确应用:

bse__throw_CUDAHOST_FAILED(::cudaSetDeviceFlags(nFlagsOfDesire));
bse__throw_CUDAHOST_FAILED(::cudaSetDevice(nDevice));

unsigned int nDeviceFlagsActual = 0;
bse__throw_CUDAHOST_FAILED(::cudaGetDeviceFlags(&nDeviceFlagsActual));
bse__throw_IF(nFlagsOfDesire != nDeviceFlagsActual);

此外,talonmies 的评论显示了解决 ::cudaHostUnregister 错误的方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-27
    • 1970-01-01
    • 1970-01-01
    • 2018-04-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多