在查找数组的最大元素时比较 2 种不同架构上的 2 种不同场景

Comparing 2 different scenarios on 2 different architectures when finding the max element of an array

假设我们有一个经典场景,我们需要找到数组的最大元素(仅限整数),而不是它的位置。以下 2 个代码示例(位于 'for' 循环内)在 CPU 上运行速度更快,哪个在 GPU 上运行更快,为什么?

if( array[i] > max)
  max = array[i];

max = 0.5 * ( a + b + abs(a-b));      //Where 'a' and 'b' refer to 'max' and 'array[i]'

此外,在第二段代码中真正困扰我的是 'abs' 函数调用。有没有办法只用算术表达式计算一个数的绝对值?

根据工作完成时间在 cpu 和 gpu 上对其进行负载平衡。

假设 tx 是 cpu 时间,ty 是 gpu 时间,wx 是 cpu 工作份额百分比,wy 是 gpu 工作份额百分比。

Iteration 1
wx=0.5;  wy=0.5; // just a guess as 1 / (total number of devices)

Iteration 2
px= wx/tx; ----> compute power of cpu
py= wy/ty; ----> compute power of gpu
because doing work in less time means more


ptotal=px+py;

wx=px/ptotal;
wy=py/ptotal;

setting work share to exact power can alternate next shares
so you may need relaxation constant


wx=0.3 * (px/ptotal);
wy=0.3 * (py/ptotal);

so small changes in instantenous compute power won't bug this.

Iteration 3:

px= wx/tx;
py= wy/ty; 

ptotal=px+py;

wx=0.3 * (px/ptotal);
wy=0.3 * (py/ptotal); 

但在 opencl 中,您会为他们提供适当的本地工作大小,并且工作共享必须在解决本地工作大小的过程中完成。

global_range_x= nearest_multiple_of_256(wx * total_global_range);
global_range_y= nearest_multiple_of_256(wy * total_global_range);

如果全局范围的总和等于总范围,则可以根据其他设备的总范围计算每个设备的偏移量。

如果 cpu 的计算范围为 768,而 gpu 的范围为 256,您可以将它们的全局偏移设置为 0(cpu) 和 768(gpu),这样它们就不会重叠。

我认为您真的想问的是无分支与分支方式m = max(m, array[i])。根据优化设置,C 编译器已经将 if() 版本编译为无分支代码(使用 cmov)。它甚至可以自动向量化为压缩比较或压缩最大函数。

你的 0.5 * abs() 版本显然很糟糕(比条件移动慢很多)因为它转换为 double 并返回。而不是右移除以二。

参见 the Godbolt Compiler Explorer 上的汇编:

// auto-vectorizes to PMAXSD, or without SSE4.1, to pcmpgt / pand/por emulation of the same
int maxarray_if(int arr[], int n) {
  int result = arr[0];
  for (int i=0 ; i<n; ++i) {
    int tmp = arr[i];
    if (result < tmp)
      result = tmp;
  }
  return result;
}

gcc 5.3 -O3 -march=haswell -mno-avx 自动矢量化内循环:

.L13:
    add     eax, 1
    pmaxsd  xmm0, XMMWORD PTR [rdx]
    add     rdx, 16
    cmp     r8d, eax
    ja      .L13

比。 FP 版本:

    ... whole bunch of integer crap
    cvtsi2sd        xmm0, eax
    mulsd   xmm0, xmm1
    cvttsd2si       eax, xmm0

所以FP版明显是垃圾

对于任何目标架构,您都会得到类似的结果。转换 to/from double 不会消失。 gcc 甚至使用 -ffast-math.

保持它