Opencl,整数向量元素的总和

Opencl, sum of integer vector elements

问题:

在 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 到 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-compiler 无法使用 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

OS:64位windows10

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

日期:2015 年 9 月 23 日

对比:

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

java1 上的串行代码。8_25 平均需要 16.5 毫秒。

Java-1.8 的 IntStream 平均需要 13.5 毫秒 (X.reduce(0, Integer::sum))

本题并行代码平均耗时12.5ms (使用单个工作组

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

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

mfa的回答:

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

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

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

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

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

你能post也请问这个时间吗?

编辑:添加更多信息。

Double version took 13.2 ms on average while float version took 12.2 ms on average but Im not sure if float addition keeps integers' quantum steps always. Could its precisiton at big floats be enough for it to add 1.0 or 0.0 ?

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

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

做归约的速度是多少?也许毕竟不是那么糟糕。

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

此外,如果您真正想要减少多个项目,而不是单个 int4。然后将它们加到 long4 space 中,然后只减少最后一个。

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;

注意:如果这是您正在执行的唯一操作,内存瓶颈很可能是这里的主要问题。所以测量内核执行时间会给出一个有很大偏差的结果。