Opencl 为什么是“;否则”;语句(在GPU代码中)会将性能减半

Opencl 为什么是“;否则”;语句(在GPU代码中)会将性能减半,opencl,gpu,gpgpu,Opencl,Gpu,Gpgpu,我读了这篇文章: 有人加了一条评论,他写道: 由于GPU是SIMD,任何带有“if-else”语句的代码都会减少您的内存 表演对半。一半的内核将执行 语句,而一半的内核处于空闲状态,然后另一半处于空闲状态 堆芯将进行else计算,而前一半堆芯 保持空闲 我不明白为什么 为什么在使用if else时使用GPU(即OpenCL)性能会减半?分支通常不会影响性能,但分支分歧会影响性能。也就是说,两个线程采用不同的路径(例如,一个线程满足if条件,另一个不满足)。因为一个GPU的所有线程都执行相同的“

我读了这篇文章:

有人加了一条评论,他写道:

由于GPU是SIMD,任何带有“if-else”语句的代码都会减少您的内存 表演对半。一半的内核将执行 语句,而一半的内核处于空闲状态,然后另一半处于空闲状态 堆芯将进行else计算,而前一半堆芯 保持空闲

我不明白为什么


为什么在使用
if else
时使用GPU(即OpenCL)性能会减半?

分支通常不会影响性能,但分支分歧会影响性能。也就是说,两个线程采用不同的路径(例如,一个线程满足
if
条件,另一个不满足)。因为一个GPU的所有线程都执行相同的“代码行”,所以有些线程在执行不属于其路径的代码时必须等待。
事实并非如此,因为只有一个warp(NVIDIA)或wavefront(AMD)中的所有线程执行相同的“代码行”。(目前,NVIDIA GPU的扭曲尺寸为32,AMD GPU的wafefront尺寸为64。)

因此,如果内核中存在
if-else
块,最坏的情况实际上是性能下降50%。更糟糕的是:如果存在
n
可能的分支,则性能可能会下降到性能的
1/n
,而不会出现差异(即没有分支或扭曲/wafefront中的所有线程都采用相同的路径)。当然,对于这种情况,整个内核必须嵌入到
if-else
(或
开关
)构造中

但是如上所述,只有当采用不同路径的线程位于同一个warp/wafefront中时,才会发生这种情况。因此,由您来编写代码/重新排列数据/选择算法/。。。尽量避免分支发散


Tl;DR:可以有分支,但如果不同的线程采用不同的分支,则它们必须位于不同的扭曲/扭曲中,以避免分歧,从而导致性能损失。

GPU的设计本质上不支持分支操作,你应该考虑把你的代码修改成这个特性。大多数时候,人们一看到GoPU就害怕分支。然而,大多数时候内核运行的性能远未达到峰值,但由于内存带宽、共享内存或缺少可用的扭曲,吞吐量受到限制。那么分歧对性能的影响就非常有限了。@janucas:你说得对,几乎从来没有达到过性能的峰值,但分支分歧会使它变得更糟。当然,对于一个可以运行在峰值性能附近的内核来说,性能的二等分会导致更大的性能损失,但只会导致绝对数,而不是相对数。我认为不必过分担心发散的一个更好的理由是,内核通常只在内核的一小部分分裂成分支(例如,只有10%受到发散的影响,因此性能下降很低)@JanLucas:但考虑差异从来都不是坏事,因为它常常伴随着糟糕的数据布局,导致不平衡的内存访问,从而浪费宝贵的内存带宽。有时候,一点点差异正是你为更好的数据布局付出的代价。如果您可以在一个好的数据布局和一些差异以及没有差异的坏数据布局之间进行选择,那么几乎总是选择好的数据布局。分支发散通常不会使情况变得更糟,例如:如果受内存带宽的限制,引入一些额外的发散通常不会降低性能。如果将不执行任何操作的全失速周期替换为执行部分活动扭曲的周期,则不会降低性能。我是否正确理解了这一点,如果我说的(伪)代码读取if(a==true){//Do something}else{//Do something}做某事的人必须等待做某事的人?这意味着,如果我只使用简单的if语句,比如使用if(A==true){A=1}else{A=2}设置某个变量,就不会出现疯狂的性能损失。只有在if语句中存在大量代码时,才会导致性能损失?