【问题标题】:OpenCL - Method to perform a reductionOpenCL - 执行缩减的方法
【发布时间】:2017-01-24 04:30:18
【问题描述】:

following post,我尝试实现一个数组的求和 使用这个内核代码:

 #pragma OPENCL EXTENSION cl_khr_int64_base_atomics : enable

__kernel void sumGPU ( __global const long *input, 
               __global long *finalSum
               )
 {
  uint local_id = get_local_id(0);
  uint group_size = get_local_size(0);

  // Temporary local value
  local long tempInput;

  tempInput = input[local_id];

  // Variable for final sum 
  local long totalSumIntegerPart[1];

  // Initialize sums
  if (local_id==0)
    totalSumIntegerPart[0] = 0;

  // Compute atom_add into each workGroup 
  barrier(CLK_LOCAL_MEM_FENCE);

  atom_add(&totalSumIntegerPart[0], tempInput);

  barrier(CLK_LOCAL_MEM_FENCE);

  // Perform sum of each workGroup sum
  if (local_id==(get_local_size(0)-1))
    atom_add(finalSum, totalSumIntegerPart[0]);

}                   

finalSum 的值不是预期值(我最初将input 数组设置为:

 for (i=0; i<nWorkItems; i++)
    input[i] = i+1;

所以,我期待 nWorkItems = 1024 : finalSum = nWorkItems*(nWorkItems+1)/2=524800

实际上,我得到了finalSum = 16384

我通过sizeWorkGroup = 16nWorkItems = 1024 得到这个结果。

奇怪的是,sizeWorkGroup = 32nWorkItems = 1024,我得到了另一个值:finalSum = 32768

我不明白最后一条指令(它应该计算每个部分和的总和,即每个工作组的总和):

// Perform sum of each workGroup sum
  if (local_id==(get_local_size(0)-1))
    atom_add(finalSum, totalSumIntegerPart[0]);

确实,我原以为指令 atom_add(finalSum, totalSumIntegerPart[0]); 将独立于 local_id if condition

最重要的是这条指令必须执行“number of workGroups”次(假设 finalSum 是所有工作组之间的共享值,不是吗?)。

所以我想我可以替换:

// Perform sum of each workGroup sum
  if (local_id==(get_local_size(0)-1))
    atom_add(finalSum, totalSumIntegerPart[0]);

 // Perform sum of each workGroup sum
      if (local_id==0)
        atom_add(finalSum, totalSumIntegerPart[0]);

任何人都可以使用我的参数(sizeWorkGroup = 16nWorkItems = 1024)帮助找到正确的值,即finalSum 等于524800

或者向我解释为什么这个最后的总和表现不佳?

更新:

这是following link 上的内核代码(它与我的略有不同,因为atom_add 这里只为每个工作项增加1):

kernel void AtomicSum(global int* sum)

{
 local int tmpSum[1]; 
 if(get_local_id(0)==0){
 tmpSum[0]=0;}

barrier(CLK_LOCAL_MEM_FENCE);         
atomic_add(&tmpSum[0],1);         
barrier(CLK_LOCAL_MEM_FENCE);    

if(get_local_id(0)==(get_local_size(0)-1)){
  atomic_add(sum,tmpSum[0]);
 }

}

这是一个有效的内核代码吗?

也许解决方案可以放在我的内核代码的开头:

unsigned int tid = get_local_id(0);
unsigned int gid = get_global_id(0);
unsigned int localSize = get_local_size(0);
// load one tile into local memory
int idx = i * localSize + tid;
localInput[tid] = input[idx];

我将对其进行测试并随时通知您。

谢谢

【问题讨论】:

    标签: c synchronization opencl atomic reduction


    【解决方案1】:

    这一行是错误的:

    tempInput = input[local_id];
    

    应该是:

    tempInput = input[get_global_id(0)];
    

    您总是对输入的第一个区域求和,这与您的奇怪结果一致。以及为什么它取决于工作组大小的参数。

    16*16*64 = 16384
    32*32*32 = 32768
    

    你的代码也可以简化一点:

      uint local_id = get_local_id(0);
    
      // Variable for final sum 
      local long totalSumIntegerPart;
    
      // Initialize sums
      if (local_id==0)
        totalSumIntegerPart = 0;
    
      // Compute atom_add into each workGroup 
      barrier(CLK_LOCAL_MEM_FENCE);    
      atom_add(&totalSumIntegerPart, input[get_global_id(0)]);    
      barrier(CLK_LOCAL_MEM_FENCE);
    
      // Perform sum of each workGroup sum
      if (local_id==0)
        atom_add(finalSum, totalSumIntegerPart);
    

    我不会像你那样滥用原子,因为它们不是最有效的归约方式。使用适当的减速方法,您可能可以将速度提高 10 倍。但是,它可以作为 PoC 或用于学习本地内存和 CL。

    【讨论】:

    • 谢谢,我理解我奇怪的结果。但是从simpleopencl.blogspot.fr/2013/04/… 开始,他们建议使用本地内存来提供更好的性能。我将更新放在他们使用的内核代码之上。
    • 你想说:“ atom_add(&totalSumIntegerPart, input[ get_global_id(0)]); ”而不是“ atom_add(&totalSumIntegerPart, input[get_global_size(0)]); ”,不是吗?
    猜你喜欢
    • 1970-01-01
    • 2017-05-30
    • 2013-05-14
    • 1970-01-01
    • 1970-01-01
    • 2015-04-17
    • 1970-01-01
    • 2013-01-06
    • 1970-01-01
    相关资源
    最近更新 更多