C++ OpenCL-矢量化与线程内for循环

C++ OpenCL-矢量化与线程内for循环,c++,kernel,opencl,C++,Kernel,Opencl,我有一个问题,我需要并行处理已知数量的线程(很棒),但每个线程的内部迭代次数可能相差很大(不是很棒)。在我看来,这样做可以更好地执行如下内核方案: __kernel something(whatever) { unsigned int glIDx = get_global_id(0); for(condition_from_whatever) { }//alternatively, do while } 其中,id(0)是事先已知的,而不是: __kernel s

我有一个问题,我需要并行处理已知数量的线程(很棒),但每个线程的内部迭代次数可能相差很大(不是很棒)。在我看来,这样做可以更好地执行如下内核方案:

__kernel something(whatever)
{
   unsigned int glIDx = get_global_id(0);

   for(condition_from_whatever)
   {

   }//alternatively, do while

}
其中,id(0)是事先已知的,而不是:

__kernel something(whatever)
{
   unsigned int glIDx = get_global_id(0);
   unsigned int glIDy = get_global_id(1); // max "unroll dimension"

   if( glIDy_meets_condition)
      do_something();
   else
      dont_do_anything();

}
根据本讨论,必须在所有可能的glIDy范围内执行,且无法提前终止:

我似乎找不到关于内核中动态大小的forloops/do while语句的成本的任何具体信息,尽管我确实在Nvidia和AMD的SDK的内核中随处可见。我记得读过一些关于内核内条件分支越不定期,性能就越差的文章

实际问题:

在GPU架构上有没有比我提出的第一个方案更有效的方法来处理这个问题

我也愿意了解关于这个话题的一般信息


谢谢。

我更喜欢第二个版本,因为
for
在迭代之间插入了错误的依赖关系。如果内部迭代是独立的,则将每个迭代发送到不同的工作项,并让OpenCL实现确定如何最好地运行它们

两个警告:

  • 如果平均迭代次数明显低于最大迭代次数,那么这可能不值得额外的虚拟工作项
  • 您将有更多的工作项,您仍然需要计算每个工作项的条件。。。如果计算条件复杂,这可能不是一个好主意。
    • 或者,您可以将索引展平到x维度,将所有迭代分组到同一个工作组中,然后仅对每个工作组计算一次条件,并使用本地内存+屏障进行同步

    • 我认为这个问题没有一个通用的答案。这取决于你的问题

      但是,以下是有关此主题的一些注意事项:

      for-loop/if-else语句可能会也可能不会对内核的性能产生影响。事实上,性能成本不是在内核级别,而是在工作组级别。工作组由一个或多个扭曲(NVIDIA)/波前(AMD)组成。这些扭曲(我将保留英伟达术语,但它完全相同的AMD)在锁定步骤中执行。 因此,如果在扭曲中由于if-else(或具有不同迭代次数的for循环)而出现分歧,则执行将被序列化。也就是说,沿着第一条路径的这个扭曲内的线程将完成它们的工作,而其他线程将空闲。一旦完成任务,这些线程将空闲,而其他线程将开始工作

      如果需要使用屏障同步线程,则这些语句会出现另一个问题。如果不是所有线程都达到了障碍,那么您将有一个未定义的行为

      现在,知道了这一点,并且根据您的具体问题,您可能能够以这样一种方式对线程进行分组,即在工作组中没有分歧,尽管您在工作组之间会有分歧(没有影响)

      同时知道一个warp由32个线程和64个波前组成(可能在旧的AMD GPU上没有-不确定),您可以使组织良好的工作组的大小等于或是这些数字的倍数。请注意,由于还应考虑其他一些问题,因此它相当简化。例如,请参阅Chanakya.sun给出的答案(也许在该主题上进行更多挖掘会更好)

      如果您的问题不能按刚才描述的方式组织,我建议考虑使用OPENCL,它在处理分支方面相当好。如果我记得清楚的话,通常每个工作组都有一个工作项。在这种情况下,最好查看英特尔和AMD的CPU文档。我也非常喜欢第6章,它解释了在编程时将OCL与GPU和CPU一起使用的区别

      我也喜欢。本文主要讨论如何通过简化GPU来提高性能(这不是您的问题),但本文的最后一部分也将探讨CPU的性能


      最后,关于您对@Oak提供的关于“设备内线程队列支持”的回答的评论,该支持实际上被称为动态并行。此功能显然可以解决您的问题,但即使使用CUDA,您也需要具有3.5或更高功能的设备。因此,即使是具有开普勒GK104体系结构的NVIDIA GPU也不支持它(功能3.0)。对于OCL,动态并行性是标准版本2.0的一部分。(据我所知,目前还没有实现)。

      假设x维度提供了足够的并行性,那么在可变大小的维度上循环的方法应该很好。请注意,整个内核时间将由运行时间最长的循环决定。如果你可以生成足够多的线程,或者如果你有其他并发内核,可以开始使用从快速线程组中释放出来的资源,这可能会很好。我认为解决这些问题的一般策略,我已经发现,其他人似乎在我所见过的各种开源软件中使用,就是要有一个主机端的线程管理套件,并有效地将计算分解为更小的迭代集,并检查线程是否在这两者之间终止。这方面的问题包括大量主机设备IO。对我来说,我列出的两个方案之间的区别是,第一个方案可能比第二个方案浪费更少的电力。我在开普勒的NVIDIA幻灯片中确实看到了设备内线程排队支持(一个线程可以排队另一个线程),除非我遗漏了什么,否则可以有效地解决这个问题。但我认为这并没有进入AMD的硬件(或者如果很快会),所以看到Supper的机会很小