【问题标题】:Switch host functions depending on CUDA compute capability at runtime在运行时根据 CUDA 计算能力切换主机功能
【发布时间】:2013-06-14 12:49:35
【问题描述】:

我目前有一个主机函数,其中包括一个循环和各种 CUBLAS 调用。现在可以访问 CC 3.5 设备,我可以使用动态并行编写一个更高效的内核。但是,我想继续支持 CC

-gencode arch=compute_30,code=sm_30 -gencode arch=compute_35,code=sm_35

我想继续生成支持这两种架构的单个二进制文件,但我想不出在主机代码中切换它的方法。 NVCC 肯定无法为主机 AFAIK 上的任何内容生成编译后的代码映像。

这不好(而且非常丑陋),因为为 CC

cudaGetDevice (&current_device);
cudaGetDeviceProperties (&current_device_properties, current_device);
if (current_device_properties.major < 3 && ... etc) {
  ...
}
else ...

__CUDACC__ 或 __CUDA_ARCH__ 在这里也没有用。

我的猜测是这是不可能的,我将不得不简单地开始编译单独的二进制文件并在预处理器中切换架构。但是,如果有人能想到什么,那就太好了。

【问题讨论】:

    标签: c++ c cuda gpgpu


    【解决方案1】:

    这取决于你的目标是什么。您似乎在这里询问两种不同的情况。

    首先,如果您认为用户可能使用不支持 CC 3.5 的 nvcc 编译代码,那么您需要对 CUDA_ARCH 使用预处理器检查来测试计算能力并防止其尝试编译不受支持的代码。

    其次,如果您打算编译代码以同时包含 CC 3.5 和更低功能的实现,您应该使用 cudaGetDeviceProperties 检查来选择正确的主机实现。

    如果您同时需要这两者,您可能需要使用类似这样的实现。

    cudaGetDevice (&current_device);
    cudaGetDeviceProperties (&cdp, current_device);
    if (cdp.major < 3 || (cdp.major >= 3 &&  cdp.minor < 5)) {
      //loop and CUBLAS
    }else {
      kernel35<<<>>>();
    }
    

    同样,您的内核必须由__CUDA_ARCH__ &gt;= 350 保护。

    #if (__CUDA_ARCH__ >= 350)
    __global__ void kernel35()
    {
      ...
    }
    #else
    __global__ void kernel35()
    {
      //fake stub kernel to allow non 35 compatible nvcc to build the code
    }
    #endif
    

    另外,我想您已经测试过新内核效率更高,但如果提前知道迭代次数,动态并行几乎总是比从 CPU 正确启动要慢。在我的测试中高达 40%,因此我建议在切换到 Kepler GPU 之前彻底测试性能。

    编辑: 我突然想到,更兼容、更安全的选择是像这样写第二部分。

    __global void kernel35(){
      #if (__CUDA_ARCH__ >=350 )
      ...
      #else
      //stub
      #endif
    }
    

    【讨论】:

    • 我确实想要两者同时进行。我之前的想法的错误是 _CUDA_ARCH_ 在设备内核中可用,而不是在具有 nvcc 转向编译的主机功能之外。谢谢,这会很好的。
    • 现在我接受了,我阅读得更彻底,并且我看到您关于 DP 比正确的 CPU 内核启动慢的评论。这很有趣。我已经测试了我的 DP,它的速度要快得多。我确实提前知道迭代次数,并且我的函数非常适合标准内核,除了它涉及三个 CUBLAS 调用,如果没有 CC \geq 3.5/DP,则无法从内核中调用。如果我对此有误,请告诉我。
    • 你引导我做正确的事。由于在我的情况下每个线程都是完全隔离的,而且我的 CUBLAS 调用都是相对简单的 1 级,我可以简单地自己在每个线程本地的内核中编写简单的迭代 blas 例程。我认为这应该工作得很好,我从来没想过这样做。
    • 在您描述的情况下,使用动态并行性可能会加快速度。我所指的情况是使用它代替 CPU 上的循环,例如 for 1-100{ kernel&lt;&lt;&lt;&gt;&gt;&gt;()},,在这种情况下,cpu 控制速度更快。要检查的一件事是循环中是否有 cudaThreadSynchronize 调用。如果这样做,请尝试删除它,如果动态并行对您有用,您可能实际上并不需要它,并且您可能会因为删除它而获得显着的性能改进。
    猜你喜欢
    • 2015-05-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多