【问题标题】:Opencl, sum of integer vector elementsOpencl,整数向量元素之和
【发布时间】:2015-09-23 13:30:58
【问题描述】:

问题:

在 Opencl 1.2 中,没有像

这样的内置函数
long sum_int4_elements_into_long(int4 v);

我的尝试:

所以我使用下面的代码(预取的展开循环的内部部分)

// acc is long
int4 tmp=arr[i+7];
acc+=tmp.x+tmp.y+tmp.z+tmp.w;
tmp=arr[i+6];
acc+=tmp.x+tmp.y+tmp.z+tmp.w;
tmp=arr[i+5];
acc+=tmp.x+tmp.y+tmp.z+tmp.w;
tmp=arr[i+4];"
acc+=tmp.x+tmp.y+tmp.z+tmp.w;
tmp=arr[i+3];
acc+=tmp.x+tmp.y+tmp.z+tmp.w;
tmp=arr[i+2];
acc+=tmp.x+tmp.y+tmp.z+tmp.w;
tmp=arr[i+1];
acc+=tmp.x+tmp.y+tmp.z+tmp.w;
tmp=arr[i];
acc+=tmp.x+tmp.y+tmp.z+tmp.w;       

将整数数组 (int4 arr) 的所有元素求和(减少)到单个长变量中,与串行代码相比,速度提升仅为 +%20 到 +%30。如果它可以启用 SSE 或 AVX,它会快得多。

也试过了:

使用纯整数累加器将求和运算速度提高了 3 倍,但整数溢出,所以我只能使用长变量。然后我尝试使用 long4 和 long2 变量作为

   // acc is a long4, arr is an int4 array
   acc+=convert_long4(arr[...]) 

  //  acc is a long2, arr is an int2 array
   acc+=convert_long2(arr[...])   

但它失败并锁定了计算机(检查了索引和限制,没有问题)所以在与 AMD CPU 的硬件指令映射下,longn to intn 一定有问题。

澄清:

必须有一些等效的 AMD 和 INTEL 的 SIMD(SSE 或 AVX)汇编指令用于

        32 bit integers in a 128-bit SSE register
         |       |       |      |        
  acc+=tmp.x + tmp.y + tmp.z + tmp.w
   ^    
   | 
  64-bit register 

但不知何故opencl没有映射到这个或者我没有打包C99 指令足够好,因此 cl 编译器无法使用 SSE/AVX 指令。

最接近的内置浮动版本是

 acc=dot(v,(float4)(1,1,1,1));

但我需要一个整数版本,因为 fp 需要 Kahan 的求和校正,这需要额外的时间。

编辑:

我不确定 int+int+int+int 是否会有适当的 long 结果,或者只是将 int 溢出到 long 中。

Opencl 版本:1.2 在 CPU 上运行(Java ----> Jocl)

CPU:AMD fx-8150

操作系统:64 位 windows 10

驱动程序:amd 的最新驱动程序。

日期:23.09.2015

比较:

16M 32 位整数,FX8150 @ 3300MHz(使用 8 个内核中的 6 个)

java 1.8_25 上的串行代码平均需要 16.5 毫秒。

Java-1.8 的 IntStream 平均耗时 13.5 ms (X.reduce(0, Integer::sum))

本题中的并行代码平均需要 12.5 毫秒 (使用单个工作组

本题的并行代码平均耗时 5.8 毫秒 (使用四个工作组

并行但溢出的非长版本需要 5 毫秒。 (达到内存带宽)

mfa 的回答:

 acc=dot(v,(double4)(1,1,1,1));

平均需要 13.5 毫秒,但浮动版本平均需要 12.2 毫秒。

我不确定浮点数是否可以始终保持其精度以将 1.0(甚至 0.0)添加到非常大的 fp 数。

【问题讨论】:

标签: integer sum opencl long-integer reduce


【解决方案1】:

做减法的速度是多少?或许毕竟没那么糟糕。

long4 lv = (long4)v;
long2 t = lv.xy + lv.zw;
acc += t.x + t.y;

另外,如果你真正想要的是减少多个项目,而不是单个 int4。然后将它们相加在 long4 空间中,然后仅减少最后一个。

long4 sums = long4(0);
sums += convert_long4(arr[0]);
sums += convert_long4(arr[1]);
sums += convert_long4(arr[2]);
...
sums += convert_long4(arr[N-1]);
sums += convert_long4(arr[N]);
long2 t = sums.xy + sums.zw;
long res = t.x + t.y;

注意:如果这是您正在执行的唯一操作,那么内存瓶颈可能是这里的主要问题。所以测量内核执行时间会得到一个高度偏差的结果。

【讨论】:

  • 我起初尝试过,convert_long4() 在我的系统中失败并导致应用程序崩溃。可能是因为不完整的操作系统驱动程序 idk。
  • 但是 .xy + .zw 的东西一定更好。我会试试的。我的必须是 4 条指令,而你的必须只有 2 条。
  • tmp=convert_long4(arr[j]); t=tmp.xy+tmp.zw; acc+=t.x+t.y;" 使用八个工作组平均花费 6.7 毫秒,这比其他解决方案慢,并且似乎受到 convert_long4 部分的瓶颈。没有这个,代码将无法编译。
  • 那么……减少工作有效吗? .xy + .zw & .x+.y。理论上应该更好,实际上我还没有测试过自己。并且还将取决于编译器优化。如果这是您在内核中执行的唯一操作,则还需要考虑内存瓶颈。
【解决方案2】:

4 个整数的总和将适合双精度浮点数。你试过了吗?

   double acc;
   acc=dot(v,(double4)(1,1,1,1));

您也可以发布这个时间吗?

编辑:添加更多信息。

双版本平均耗时 13.2 毫秒,而浮动版本平均耗时 12.2 平均毫秒,但我不确定浮点加法是否保持整数 总是量子步骤。它在大浮动时的精度是否足以满足 加1.0还是0.0?

额外的精度肯定会增加额外的 1ms。在一些较旧的 AMD GPU 硬件上,双重操作实际上需要两倍的时间,因为它们实际上同时使用了两个浮点寄存器。当您考虑从数学上讲双精度操作最多包含 8 个单独的单精度操作时,您测量的性能略有下降也可以解释。总体而言,我认为您的 CPU 在相对于浮点数的双精度方面做得不错。

如果总和保持在 24 位以下,单精度不会溢出。 More about this here. 双精度允许 54 位精度 (here)。当您知道总和会很小时,也许值得拥有一个单独的“小”内核?

【讨论】:

  • 好的,我将为此将整数转换为双精度数(在内核中),并尽快查询时间。
  • 双版本平均耗时 13.5 毫秒,而浮点版本平均耗时 12.2 毫秒,但我不确定浮点加法是否始终保持整数的量子步长。它在大浮点数上的精度是否足以让它增加 1.0 或 0.0 ?
  • 我已经有了一个小内核,但我也需要一个大内核。如果我可以比这更快地使用双精度,我将为浮点加法器添加 Kahan Summation 校正。 (会使这更慢)en.wikipedia.org/wiki/Kahan_summation_algorithm 我最初需要的是 SIMDify 操作不会比 tmp.x+tmp.y+tmp.z+tmp.w 版本花费更长的时间。还是谢谢你。
猜你喜欢
  • 2012-08-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-09
  • 1970-01-01
  • 2014-02-05
相关资源
最近更新 更多