【问题标题】:OpenCL, substituting branches with arithmetic operationsOpenCL,用算术运算代替分支
【发布时间】:2018-12-17 03:50:41
【问题描述】:

以下问题更多与设计有关,而不是实际编码。不知道这个问题有没有专门的术语,我就举个例子吧。

我有一些 openCL 代码根本没有优化,在内核中基本上有一个类似于以下的 switch 语句

switch(const) {
   case const_a : do_something_a(...); break;
   case const_b : do_something_b(....); break;
   ... //etc
}

我无法写出实际的语句,因为它很长。作为一个简单的例子,考虑下面的 switch 语句:

int a;
switch(input):
  case 13 : {a = 3; break;}
  case 1 : {a = 7; break;}
  case 23 : {a = 1; break;}
  default : {...}

问题是……用类似这样的表达式来改变这样的开关会更好

a = (input == 13)*3 + (input == 1)*7 + (input == 23)

?

如果不是,是否有可能让它更高效?

您可以假设input 仅在 switch 语句的情况下取值。

【问题讨论】:

  • 这是否意味着你没有办法建议更有效地写那一点?
  • 完成,问题已编辑。
  • 这就像在黑暗中拍摄。这就是我要做的,如果可能的话,看看生成的代码。如果您看不到编译器在做什么,那么您可以进行基准测试,看看是否有任何好处。如果 OpenCL 驱动程序经过优化,它可能会发现这些是常量并生成多个二进制文件(称为函数专业化),您可以这样做,使用 MACROS 创建多个内核并为每个常量 -DMYCONST=13 构建程序,并且条件应该通过以下方式进行优化任何体面的编译器。

标签: parallel-processing opencl gpu


【解决方案1】:

您发现了一个有趣的问题,GPU 编译器正在努力解决这个问题。一般建议是尽量不要分支。使之成为可能的技巧是拆分内核(如上所述)和预处理器(程序时定义)。 GPU算法开发的研究基本上都是从这个公理出发的。

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

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

kernel void foo(const int *t, ...)
  ...
  a = tbl[input];

这个案例也有一些问题,具体取决于硬件、输入和问题大小。

如果没有更具体的上下文,我可以想出一个案例,其中任何一个都可以运行良好或运行不佳。

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

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

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

  • 谓词代码(您的(input == 13)*3 ... 代码)。

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

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

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

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

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

我现在受到启发去研究这个临界值。 :-)

【讨论】:

  • 我在考虑 LUT 方法,问题是我不知道如何将这些常量映射到索引。
  • 出于好奇,问题是否有特定名称?所以也许我可以查一些例子。
  • Divergence 通常是 SIMD/SIMT 硬件中用于工作项(SIMD 通道)采用不同路径的术语。没有简单的方法可以做好,上面的例子都试图减少影响。
猜你喜欢
  • 2014-11-13
  • 2012-06-14
  • 2016-03-10
  • 1970-01-01
  • 2020-10-05
  • 2019-02-07
  • 2012-12-16
  • 2022-01-07
  • 1970-01-01
相关资源
最近更新 更多