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 = 1024finalSum = 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];


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

谢谢

最佳答案

这行是错误的:

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都可以。

09-11 02:54