AMD提出的OpenCL中的两阶段缩减示例中的内核属性

时间:2014-12-11 09:42:33

标签: opencl gpu reduction

我在理解here所描述的两阶段缩减算法时遇到了一些问题。

__kernel
void reduce(__global float* buffer,
        __local float* scratch,
        __const int length,
        __global float* result) {

  int global_index = get_global_id(0);
  float accumulator = INFINITY;
  // Loop sequentially over chunks of input vector
  while (global_index < length) {
    float element = buffer[global_index];
    accumulator = (accumulator < element) ? accumulator : element;
    global_index += get_global_size(0);
  }

  // Perform parallel reduction
  int local_index = get_local_id(0);
  scratch[local_index] = accumulator;
  barrier(CLK_LOCAL_MEM_FENCE);
  for(int offset = get_local_size(0) / 2;
      offset > 0;
      offset = offset / 2) {
    if (local_index < offset) {
      float other = scratch[local_index + offset];
      float mine = scratch[local_index];
      scratch[local_index] = (mine < other) ? mine : other;
    }
    barrier(CLK_LOCAL_MEM_FENCE);
  }
  if (local_index == 0) {
    result[get_group_id(0)] = scratch[0];
  }
}

我理解基本的想法,但我不确定while循环。据我所知,属性 length 指定缓冲区中元素的数量,即我想要处理多少元素。但 get_global_size 会返回全局工作项数。 Aren&lt; 长度 get_global_size 等于?这意味着while循环条件只能满足一次。我们不应该使用 get_local_size 而不是 get_global_size 吗?

3 个答案:

答案 0 :(得分:3)

  

Aren&t [{1}}和length等于?

不一定。通常比数据元素启动更少的工作项,并且每个工作项处理多个元素。这样,您就可以将输入数据大小与工作项数量分离。

在这种情况下,以下内容:

get_global_size

执行驻留在全局内存中的数组的最小缩减。基本上,工作组将&#34;滑动&#34;在输入向量上,并且在每次迭代时,每个工作项都将更新其最小值。

这是一个虚拟的数字示例,我们在20个元素的数组上启动2个工作组,包含4个工作项。 // Loop sequentially over chunks of input vector while (global_index < length) { float element = buffer[global_index]; accumulator = (accumulator < element) ? accumulator : element; global_index += get_global_size(0); } 表示输入数组中的第N个元素,xNaN分别代表工作组bNa中的第N个工作项。因此,b条件满足2到3次,具体取决于工作项ID:

while

length: 20 get_global_size(): 8 get_local_size(): 4 x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 x11 x12 x13 x14 x15 x16 x17 x18 x19 Input array --------------------------------------------------------------------- Iterations a0 a1 a2 a3 b0 b1 b2 b3 0 a0 a1 a2 a3 b0 b1 b2 b3 1 a0 a1 a2 a3 2 循环结束时,每个工作项将在输入数组的子集上计算最小值。例如,while将计算a0,而min(x0, x8, x16)将计算b0

然后工作项将它们计算的最小值写入本地内存,工作组继续在本地内存中进行最小减少(使用缩减树)。他们的结果被写回全局内存,并且可能会再次使用min(x4, x12)调用内核作为min-reduce的新数组,直到最终结果为单个元素。

答案 1 :(得分:0)

全局大小可能大于,因为在OpenCL 1.x中,全局大小必须是工作组大小的整数倍。因此,全局大小可能已从数据大小(长度)向上舍入。例如,如果length为1000但工作组大小为128,那么全局大小将为1024。

答案 2 :(得分:0)

[完整描述]

<强>概述:

这是一个两阶段缩减,通过减少同步/屏障和开销,并使所有计算单元尽可能保持繁忙,优于递归多级缩减。在了解内核之前,了解主机程序设置的工作项和工作组配置以及内核的参数非常重要。在此示例中,任务是查找N个浮点数的最小值。配置如下。

<强>设定:

工作组配置是,主机设置K个工作项(K

内核的参数和维度是:1)第一个参数是N个大小的数组,包含N个数据元素(用于查找min的候选数据); 2)第二个参数是一个大小为Q的空数组; 3)length的值等于N;和4)结果是大小为P的数组。

工作流程:第1阶段

工作流程如下: 如果N%K == 0,则每个工作项最初在N / K个数据元素中找到最小值,其中数据元素彼此相隔K个项目。 while循环执行此任务。如果N%K!= 0,则某些工作项计算最小的ceil(N / K)元素,其余工作项找到最小楼层(N / K)元素。(如上面的回答Kretab所述) Chabawenizc)。

每个工作项的发现最初都存储在本地变量累加器中,然后最终保存到本地数组中。一旦所有工作项都完成了这部分工作(由屏障(CLK_LOCAL_MEM_FENCE)确保),内核开始充当递归并行缩减内核。来自特定工作组的工作项将scratchpad视为数据项数组,然后每个工作项通过迭代减少它(for循环执行此操作。阅读实际的AMD文档以获得更多解释)。

最后,结果的前P个元素将包含每个P工作组的最小值。

工作流程:第2阶段

现在第二阶段开始了;在此阶段,可以为P工作项和1个工作组调用相同的内核。结果数组这次是内核的第一个参数,一个元素数组将是内核接收最终结果的最后一个参数。

在此运行中,while循环不会执行任何重要操作,只需将缓冲区中的值复制到临时。因此,您可以提出更优化的内核,并将其用于第二阶段。