我有一个问题,我需要并行处理已知数量的线程(很棒),但每个线程的内部迭代次数可能会大不相同(不是很好)。在我看来,这样可以更好地执行这样的内核方案:
__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架构?
我也对这个主题的一般信息持开放态度。
感谢。
答案 0 :(得分:1)
我更喜欢第二个版本,因为for
在迭代之间插入了一个错误的依赖项。如果内部迭代是独立的,则将每个迭代发送到不同的工作项,并让OpenCL实现了解如何最好地运行它们。
两个警告:
答案 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的一部分。 (据我所知,还没有实施)。