根据运行时的CUDA计算功能切换主机功能

时间:2013-06-14 12:49:36

标签: c++ c cuda gpgpu

我目前有一个主机功能,包括一个循环和各种CUBLAS调用。现在可以访问CC 3.5设备,我可以使用动态并行性编写一个更高效的内核。但是,我想继续支持CC的旧功能< 3.5设备。我现在支持具有几个基本代码的相同二进制文件中的多个设备:

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

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

这不是好事(也非常丑陋),因为用户建立CC< 3.5将无法使用3.5功能构建内核:

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

__ CUDACC__或__CUDA_ARCH__也没用。

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

1 个答案:

答案 0 :(得分: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__ >= 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
}