我在理解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 吗?
答案 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个元素,xN
和aN
分别代表工作组bN
和a
中的第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循环不会执行任何重要操作,只需将缓冲区中的值复制到临时。因此,您可以提出更优化的内核,并将其用于第二阶段。