OpenCL - 双重原子操作直到极限

时间:2017-01-25 11:28:20

标签: c opencl atomic reduction

link之后,我尝试实现一个计算double数组之和的原子函数,所以我实现了自己的atom_add函数(用于double)。

这是使用的内核代码:

#pragma OPENCL EXTENSION cl_khr_fp64: enable
#pragma OPENCL EXTENSION cl_khr_int64_base_atomics : enable

void atom_add_double(__global double *val, double delta)
{
  union {
  double f;
  ulong  i;
  } old, new;

  do
  {
   old.f = *val;
   new.f = old.f + delta;
  } 
  while (atom_cmpxchg((volatile __global ulong *)val, old.i, new.i) != old.i);

}  

__kernel void sumGPU ( __global const double *input, 
               __global double *finalSum
                 )
{
  // Index of current workItem
  uint gid = get_global_id(0);

  // Init sum
  *finalSum = 0.0;

  // Compute final sum 
  atom_add_double(finalSum, input[gid]);

}                   

我的问题是内核代码产生了良好的结果,直到我达到大约100000个input数组大小的元素。

超过此限制,计算不再有效(我可以轻松检查结果,因为在我的测试用例中,我通过循环for(i=0;i<sizeArray;i++) input[i]=i+1;填充输入数组,因此总和等于{{1 }}

任何人都有这种错误吗?

我可以在内核代码中定义和放置像sizeArray*(sizeArray+1)/2这样的函数吗?

欢迎任何帮助,谢谢

2 个答案:

答案 0 :(得分:3)

@huseyin回答是正确的解决问题。

然而,我无法抗拒说“不要使用原子来减少。”

更糟糕的原子是锁定while循环并直接访问全局数据。我们可能至少谈论了10倍的性能损失。

如果可以,请使用proper automatic reduction (CL 2.0+)

__kernel void sumGPU(__global const double *input, __global double *finalSum)
{
  // Index of current workItem
  uint gid = get_global_id(0);

  // Sum locally without atomics
  double sum = work_group_scan_inclusive_add(input[gid]);

  // Compute final sum using atomics
  // but it is even better if just store them in an array and do final sum in CPU
   // Only add the last one, since it contains the total sum
  if (get_local_id(0) == get_local_size(0) - 1) { 
    atom_add_double(finalSum, sum);
  }
} 

答案 1 :(得分:1)

*finalSum = 0.0;

是所有飞行中线程的竞争条件。它使我的计算机的结果为零。删除它,从主机端初始化它。如果你的gpu是非常好的,飞行中的线程数可能高达50000甚至更多,并且每个在任何开始原子函数之前击中finalSum = 0.0但是当你通过该限制时,50001st(只是一个微不足道的数字)线程将其重新初始化为零。

然后,所有元素的总和不等于大小*(大小+ 1)/ 2,因为它从零开始(第零个元素为零)所以它实际上是

(size-1)*(size)/2
当我从内核中删除finalSum = 0.0时,

正在为我的计算机提供正确的结果。