AMD提出的OpenCL两阶段约简示例中的内核属性

AMD提出的OpenCL两阶段约简示例中的内核属性,opencl,gpu,reduction,Opencl,Gpu,Reduction,我在理解所描述的两阶段简化算法时遇到一些问题 \u内核 void reduce(uu全局浮点*缓冲区, __局部浮动*刮伤, __常量int长度, __全局浮点*结果){ int global_index=get_global_id(0); 浮动累加器=无穷大; //在输入向量块上按顺序循环 while(全局_指数

我在理解所描述的两阶段简化算法时遇到一些问题

\u内核
void reduce(uu全局浮点*缓冲区,
__局部浮动*刮伤,
__常量int长度,
__全局浮点*结果){
int global_index=get_global_id(0);
浮动累加器=无穷大;
//在输入向量块上按顺序循环
while(全局_指数<长度){
浮动元素=缓冲区[全局索引];
累加器=(累加器<元件)?累加器:元件;
全局索引+=获取全局大小(0);
}
//执行并行还原
int local_index=get_local_id(0);
划痕[局部指数]=累加器;
屏障(CLK_本地_MEM_围栏);
对于(int offset=get_local_size(0)/2;
偏移量>0;
偏移量=偏移量/2){
if(局部指数<偏移量){
浮动其他=划痕[局部索引+偏移];
浮动矿=划痕[局部指数];
划痕[局部指数]=(矿山<其他)?矿山:其他;
}
屏障(CLK_本地_MEM_围栏);
}
如果(本地_索引==0){
结果[get_group_id(0)]=划痕[0];
}
}
我理解基本思想,但我不确定while循环。就我所知,属性长度指定缓冲区中元素的数量,即我到底要处理多少个元素。但是get_global_size返回工作项的全局数量。那么,长度和get_全局大小不是相等吗?这意味着while循环条件将只满足一次。我们是否应该使用get_local_size而不是get_global_size?

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

那么
length
get\u global\u size
是否相等

不一定。通常启动的工作项少于数据元素,并且每个工作项处理多个元素。通过这种方式,您可以将输入数据大小与工作项的数量解耦

在这种情况下,应满足以下条件:

// 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);
}
while
循环完成时,每个工作项都将计算输入数组子集上的最小值。例如,
a0
将计算
min(x0,x8,x16)
b0
将计算
min(x4,x12)

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

[完整描述]

概述:

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

设置:

工作组配置为,主机设置K个工作项(K 内核的参数和维度是:1)第一个参数是一个N大小的数组,包含N个数据元素(用于查找min的候选数据);2) 第二个参数是大小为Q的空数组;3) 长度的值等于N;结果是一个大小为P的数组

工作流:第1阶段

工作流程如下所示: 如果N%K==0,则每个工作项最初会在N/K个数据元素中找到最小值,其中数据元素之间相隔K个项目。while循环完成这个任务。如果N%K!=0,一些工作项计算ceil(N/K)元素的最小值,其余工作项计算floor(N/K)元素的最小值。(如Kretab Chabawenizc在上面的回答中所解释)

每个工作项的结果最初存储在局部变量累加器中,然后最终保存到局部数组中。一旦完成了这部分工作的所有工作项(由屏障(CLK_LOCAL_MEM_FENCE)保证),内核就开始充当递归并行简化内核。来自特定工作组的工作项将SabrPad视为数据项数组,并且每个工作项通过迭代来减少它(for循环执行此操作。读取实际AMD文档以获得更多解释)。 最后,结果的前p个元素将包含每个p工作组找到的最小值

工作流:第2阶段

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


在这次运行中,while循环不会做任何有意义的事情,只会将值从缓冲区复制到scratch。因此,您可以提出一个更优化的内核,并将其用于第二阶段。

在缩减情况下,全局大小往往小于实际数据大小,因为参与每个过程缩减的工作项数量为N/2、N/4、N/8、N/16等。。因此,他们宁愿从(即:N/8)开始,然后从那里继续。在最后的循环中不要有太多的空闲工作项。我上传了用C编写的这个2阶段缩减的示例代码
// 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);
}
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