OpenCL - Vectorization vs In-thread for循环

时间:2014-01-13 03:12:03

标签: c++ kernel opencl

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

__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的全部可能范围,无法事先终止:

Killing OpenCL Kernels

我似乎无法找到有关内核中动态大小的forloops / do-while语句成本的任何具体信息,尽管我确实在Nvidia和AMD的SDK中的内核中看到它们。我记得读过一些关于内核条件分支越不规则的情况,性能越差。

实际问题:

有没有比我提出的第一个方案更有效的方法来处理GPU架构?

我也对这个主题的一般信息持开放态度。

感谢。

2 个答案:

答案 0 :(得分:1)

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

两个警告:

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

答案 1 :(得分:1)

我不认为可以给出这个问题的一般答案。这真的取决于你的问题。

但是这里有一些关于这个主题的注意事项:

for loop / if else语句可能会或可能不会对内核的性能产生影响。事实上,性能成本不是在内核级别,而是在工作组级别。工作组由一个或多个warp(NVIDIA)/ wavefront(AMD)组成。这些warp(我将保留NVIDIA术语,但它与AMD完全相同)是以锁定步骤执行的。

因此,如果在warp中由于if else(或具有不同迭代次数的for循环)而导致分歧,则执行将被序列化。也就是说,在第一条路径之后的这个warp中的线程将完成它们的工作,其他线程将空闲。一旦他们的工作完成,这些线程将闲置,而其他线程将开始工作。

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

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

还知道warp由32个线程组成,64个波前(可能不在旧的AMD GPU上 - 不确定),你可以使组织良好的工作组的大小相等或者是这些数字的倍数。请注意,它是非常简化的,因为应该考虑其他一些问题。例如,参见this question和Chanakya.sun给出的答案(也许更多关于该主题的讨论会很好)。

如果您的问题无法按照刚刚描述的那样进行组织,我建议考虑在CPU上使用OpenCL,这非常适合处理分支。如果我记得,通常每个工作组都有一个工作项。在这种情况下,最好查看Intel和AMD的CPU文档。我也非常喜欢Heterogeneous Computing with OpenCL的第6章,它解释了在编程时使用OCL与GPU和CPU之间的区别。

我也喜欢this article。这主要是关于提高GPU性能(不是你的问题)的性能提升的讨论,但本文的最后部分还讨论了CPU的性能。

最后一点,关于你对@Oak提供的关于“设备内线程排队支持”的答案的评论,这实际上称为动态并行。此功能显然可以解决您的问题,但即使使用CUDA,您也需要具有3.5或更高功能的设备。因此,即使采用Kepler GK104架构的NVIDIA GPU也不支持它(功能3.0)。对于OCL,动态并行性是标准版本2.0的一部分。 (据我所知,还没有实施)。