我试图通过简化运算符在1-D数组中找到最大值。我在OpenCL™ Optimization Case Study: Simple Reductions
中提到了这个方法以下是我的代码:
__kernel void Normallize(__global float* input, __global float* output,__global float* cmax, int rows, int cols){
int g_idx = get_global_id(0);
for(int i=0 ; i< get_global_size(0) ; i++) cmax[i] = 0;
barrier(CLK_GLOBAL_MEM_FENCE);
for(int offset = get_global_size(0)/2 ; offset >0 ; offset--){
if(g_idx < offset){
float pre = input[g_idx];
float next = input[g_idx + offset];
cmax[g_idx] = (pre > next) ? pre:next;
}
barrier(CLK_GLOBAL_MEM_FENCE);
}
output[g_idx] = cmax[0];
}
在做了一些研究之后,我仍然无法在我的代码中找出问题。
答案 0 :(得分:1)
你的意思是这个(amd gpu的%60 VALU利用率)?:
__kernel void maxping(__global __read_only float * a, __global __write_only float *b){
int threadId=get_global_id(0);
int localThreadId=get_local_id(0);
int localSize=get_local_size(0);
__local float fastMem[256];
fastMem[localThreadId]=a[threadId];
barrier(CLK_GLOBAL_MEM_FENCE|CLK_LOCAL_MEM_FENCE);
for(int i=localSize/2;i>=1;i/=2)
{
if(localThreadId<i)
{
if(fastMem[localThreadId]<fastMem[localThreadId+i])
fastMem[localThreadId]=fastMem[localThreadId+i];
}
barrier(CLK_LOCAL_MEM_FENCE);
}
if(localThreadId==0)
b[threadId]=fastMem[localThreadId];
}
其中每个组(256个线程)减少本地内存并将每个first-in-group值设置为其组的最大值。此示例包含096到4095之间的4096个元素。
对于上层内核,VALU的用法类似于:
x: idle thread
o: thread in process, m: thread in memory operation
** : m m m m m m m m m m m m m m m m
i=0 : o o o o o o o o x x x x x x x x
i=1 : o o o o x x x x x x x x x x x x
i=2 : o o x x x x x x x x x x x x x x
i=3 : o x x x x x x x x x x x x x x x
** : m m m m m m m m m m m m m m m m
但我计算更多步骤,每行跨越250个单位。
__kernel void maxpong(__global __write_only float * a, __global __read_only float *b){
int threadId=get_global_id(0);
int localSize=get_local_size(0);
int maxGroups=4096/localSize;
if(threadId==0)
{
float maxv=FLT_MIN;
for(int i=0;i<maxGroups;i++)
{
if(maxv<b[i*localSize])
maxv=b[i*localSize];
}
a[0]=maxv;
}
}
其中只有第一个线程(最好在cpu中)执行简单的max(0,1,2,...,M)并将a的第一个元素设置为max(a)。
第一个内核完成总计算的255/256。但它使每个计算单元的一半核心不受影响。因此,您可以在另一半内核中对另一个内容进行排序。这可能是另一个数组,即max()'或同一个数组的min()'或甚至同一个数组的相同最大值,但工作在其中一半而其他核工作在另一半上。
%73使用不同初始内核的max(a)的VALU利用率:
__kernel void maxping(__global __read_only float * a, __global __write_only float *b){
int threadId=get_global_id(0);
int localThreadId=get_local_id(0);
int localSize=get_local_size(0);
__local float fastMem[256];
__local float fastMem2[256];
fastMem[localThreadId]=a[threadId];
fastMem2[localThreadId]=a[threadId+2048];
barrier(CLK_GLOBAL_MEM_FENCE|CLK_LOCAL_MEM_FENCE);
for(int i=localSize/2;i>=1;i/=2)
{
if(localThreadId<i)
{
// sorting first part
if(fastMem[localThreadId]<fastMem[localThreadId+i])
fastMem[localThreadId]=fastMem[localThreadId+i];
}
else if(localThreadId>localSize-i)
{
// sorting second part
if(fastMem2[localThreadId]<fastMem2[localThreadId-i])
fastMem2[localThreadId]=fastMem2[localThreadId-i];
}
else
{
// idle thread. Free compute slot.
// can squeeze some geometry computing
// or up-sweep scan of another reduction type
}
barrier(CLK_LOCAL_MEM_FENCE);
}
if(localThreadId==0)
b[threadId]=(fastMem[localThreadId]>fastMem2[255]?fastMem[localThreadId]:fastMem2[255]);
}
这为4096个元素阵列使用了2048个线程。将0th,256th,512nd,..元素设置为各自的组最大值,然后您可以轻松检查主机端哪个更大。
仍有未使用的核心。
对于上层内核,VALU的用法类似于:
x: idle thread
o: thread in process, m: thread doing memory operation
** : m m m m m m m m m m m m m m m m
i=0 : o o o o o o o o o o o o o o o o
i=1 : o o o o x x x x x x x x o o o o
i=2 : o o x x x x x x x x x x x x o o
i=3 : o x x x x x x x x x x x x x x o
** : m m m m m m m m m m m m m m m m
但是我的步骤是log2(256)次,因此有更多的“i”步骤,并且amd硬件有64个核心,即使在一个步骤中有64个线程也可以完全服务。当我们总结该循环的所有线程使用时,它不给出%73但是当其他“warps”(其中40个)流到同一计算单元时,更多的空洞被填充,因此更多的矢量算术逻辑单元变得更常用。甚至本地内存分配部分也很重要,因为所有内核的内存操作单元都保持忙碌(全局到本地,本地到全局),而其他warp使比较单元忙。
编辑:如果您不需要全局大小为256的倍数,那么您可以在本地内存操作后添加全局ID检查,因此它不会执行未定义的行为。也许您可以使用额外的FLT_MIN值填充数组。