Parallel processing OpenCL,用算术运算替换分支

Parallel processing OpenCL,用算术运算替换分支,parallel-processing,opencl,gpu,Parallel Processing,Opencl,Gpu,下面的问题更多地与设计有关,而不是实际的编码。我不知道这类问题是否有专门术语,所以我将继续举一个例子 我有一些openCL代码根本没有经过优化,在内核中有一个与下面类似的switch语句 switch(const) { case const_a : do_something_a(...); break; case const_b : do_something_b(....); break; ... //etc } 我不能写实际的陈述,因为它很长。作为一个简单的例子,考虑下面的

下面的问题更多地与设计有关,而不是实际的编码。我不知道这类问题是否有专门术语,所以我将继续举一个例子

我有一些openCL代码根本没有经过优化,在内核中有一个与下面类似的switch语句

switch(const) {
   case const_a : do_something_a(...); break;
   case const_b : do_something_b(....); break;
   ... //etc
}
我不能写实际的陈述,因为它很长。作为一个简单的例子,考虑下面的开关语句:

int a;
switch(input):
  case 13 : {a = 3; break;}
  case 1 : {a = 7; break;}
  case 23 : {a = 1; break;}
  default : {...}
问题是。。。用这样的表达式更改这样的开关是否更好

a=(输入==13)*3+(输入==1)*7+(输入==23)

?

如果不是,是否有可能提高效率


您可以假设
input
只接受switch语句的case集合中的值。

您发现了一个GPU编译器需要解决的有趣问题。一般的建议是尽量不要分支。实现这一点的技巧是将内核拆分(如上所述)和预处理器(程序时间定义)。GPU算法开发的研究基本上就是从这个公理出发的

由于固有的差异(通道=SIMD线程/warp中的工作项),到处分支不会获得很高的效率。请记住,所有这些通道必须一起执行。因此,在一个所有人都走不同道路的开关中,其他人都会默默地等待他们的“案例”执行。现在,如果
input
始终是相同的值,那么仍然可以获胜

另一个流行的选项是表间接寻址

kernel void foo(const int *t, ...)
  ...
  a = tbl[input];
根据硬件、输入和问题大小,这种情况也有一些问题

如果没有更具体的背景,我可以想出一个例子,其中任何一个都可以运行得很好或很差

  • 切换(或大if-then-else链)

    • 优点:如果所有工作项通常采用相同的路径(
      input
      的值基本相同),那么它将是高效的。您还可以编写一个if-then-else链,将最常见的情况放在首位。(在GPU上,切换不一定像间接跳转那么容易,因为有多个工作项,它们可能会采用不同的路径。)

    • 缺点:可能会生成大量程序代码,并且可能会破坏指令缓存。根据需要评估的案例数量,各地的分支可能会有点昂贵。使用谓词代码进行计算可能会更好

  • 谓词代码(您的(输入==13)*3…代码)

    • 优点:这可能会产生更小的程序,并减少I$的压力。(查找函数以查看更通用的案例方法。)

    • 缺点:我们基本上已经下了赌注,并决定评估每一个“切换中的案例”。如果
      input
      通常是相同的值,那么我们在这里浪费时间

  • 基于查找表的方法(我的示例)

    • 优点:如果您正在评估的交换机有大量的案例(分支),但可以按整数索引,那么您可能会提前使用查找表。在某些硬件上,这意味着从全局内存(很远)读取。其他架构有专用的常量缓存,但我知道向量查找将序列化(每个通道有K个周期)。因此,它可能只比全局内存表稍微好一点。但是,生成的代码表查找将很短(I$friendly),并且随着分支(case语句)数量的增加,这将在极限内获胜。这种方法还可以很好地处理
      输入值的均匀/分散分布

    • 缺点:从全局内存读取(或从常量缓存进行序列化访问)与分支相比有很大的延迟。在某些情况下,为了消除额外的内存流量,我看到编译器将查找表转换为if-then-else/switch链。我们很少有100个元素的case语句


我现在受到启发,要去研究这个问题。:-)

这是否意味着你不可能建议写得更有效率?完成,问题编辑。这就像在黑暗中射击。这就是我要做的,如果可能的话,看看生成的代码。如果您看不到编译器在做什么,那么您可以进行基准测试,看看是否有任何好处。如果OpenCL驱动程序经过优化,它可能会发现这些是常量并生成了多个二进制文件(称为函数专用化),您也可以这样做,使用宏创建多个内核,并为每个常量构建程序
-DMYCONST=13
,任何像样的编译器都应该对条件进行优化。我在考虑LUT方法,问题是我不知道如何将这些常量映射到索引。出于好奇,这个问题有具体的名称吗?因此,也许我可以查一些例子。分歧通常是SIMD/SIMT硬件中使用的术语,用于表示工作项(SIMD通道)采用不同路径的情况。要做好这件事没有简单的方法,上面的例子都试图减少影响。