OpenCL在数组中找到max

时间:2016-04-07 02:20:27

标签: opencl

我试图通过简化运算符在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];
}

在做了一些研究之后,我仍然无法在我的代码中找出问题。

1 个答案:

答案 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值填充数组。