总和浮点数的最佳OpenCL 2内核是什么?

时间:2017-10-21 08:34:00

标签: c++ opencl gpgpu c++17 sycl

C ++ 17引入了许多支持并行执行的新算法,特别是std::reducestd::accumulate的并行版本,它允许non-deterministic行为non-commutative行为,例如作为浮点加法。我想使用OpenCL 2实现reduce算法。

英特尔有一个示例here,它使用OpenCL 2 work group内核函数来实现std::exclusive_scan OpenCL 2内核。以下是基于英特尔exclusive_scan示例的总和浮点数的内核:

kernel void sum_float (global float* sum, global float* values)
{
  float sum_val = 0.0f;

  for (size_t i = 0u; i < get_num_groups(0); ++i)
  {
    size_t index = get_local_id(0) + i * get_enqueued_local_size(0);
    float value = work_group_reduce_add(values[index]);
    sum_val += work_group_broadcast(value, 0u);
  }

  sum[0] = sum_val;
}

上面的内核有效(或似乎!)。但是,exclusive_scan要求work_group_broadcast函数将一个work group的最后一个值传递给下一个,而此内核只需要将work_group_reduce_add的结果添加到sum_val,所以atomic add更合适。

OpenCL 2提供支持atomic_int的{​​{1}}。使用atomic_int的上述内核的整数版本是:

atomic_fetch_add

OpenCL 2还提供kernel void sum_int (global int* sum, global int* values) { atomic_int sum_val; atomic_init(&sum_val, 0); for (size_t i = 0u; i < get_num_groups(0); ++i) { size_t index = get_local_id(0) + i * get_enqueued_local_size(0); int value = work_group_reduce_add(values[index]); atomic_fetch_add(&sum_val, value); } sum[0] = atomic_load(&sum_val); } ,但 支持atomic_float

实现OpenCL2内核对浮点数求和的最佳方法是什么?

1 个答案:

答案 0 :(得分:1)

kernel void sum_float3 (global float* sum, global float* values)
{
  float sum_partial = work_group_reduce_add(sum[get_global_id(0)]);
  if(get_local_id(0)==0)
    values[get_group_id(0)] = sum_partial; 
}

这具有将数据写入sum的零索引元素的竞争条件,所有工作组正在进行相同的计算,这使得该O(N * N)而不是O(N)并且需要超过1100毫秒完成1M元素数组和。

对于相同的1-M元素数组,这个(global = 1M,local = 256)

values: 1.0 1.0 .... 1.0 1.0 
sum_float2
sum: 256.0 256.0 256.0
sum_float3
values: 65536.0 65536.0 .... 16 items total to be summed by cpu 

后跟这个(global = 4k,local = 256)

{{1}}
除了第三步,

在几毫秒内做同样的事情。第一个将每个组加入到它们的group-id相关项中,第二个内核将它们加到16个值中,这16个值可以很容易地用CPU(微秒或更小)求和(作为第三步)。

程序的工作方式如下:

{{1}}

如果你需要使用原子,你应该尽量少做。最简单的例子可以是使用局部原子来对每个组的许多值求和,然后使用每个组的单个全局原子函数进行最后一步以添加所有值。我现在还没有为OpenCL准备好C ++设置,但是当你使用具有相同内存资源的多个设备(可能是流模式或SVM)时,我认为OpenCL 2.0原子会更好/或使用C ++ 17函数的 CPU 。如果你没有同时在同一区域上计算多个设备,那么我认为这些新的原子只能在已经运行的OpenCL 1.2原子之上进行微优化。我没有使用这些新的原子,所以把所有这些都当作一粒盐。