Opencl - 传输全局内存工作组+边界到本地内存

时间:2017-10-20 19:29:02

标签: c++ memory opencl

这是我制作的代码草案:

void __kernel myKernel(__global const short* input,
                       __global short* output,
                       const int width,
                       const int height){                         


    // Always square. (and 16x16 in our example)
    const uint local_size = get_local_size(0);

    // Get the work-item col/row index
    const uint wi_c = get_local_id(0);
    const uint wi_r = get_local_id(1);

    // Get the global col/row index
    const uint g_c = get_global_id(0);
    const uint g_r = get_global_id(1);

    // Declare a local array NxN 
    const uint arr_size = local_size *local_size ;
    __local short local_in[arr_size]; 

    // Transfer the global memory for into a local one.
    local_in[wi_c  + wi_r*local_size ] = input[g_c + g_r*width];

    // Wait that all the work-item are sync
    barrier(CLK_LOCAL_MEM_FENCE);

    // Now add code to process on the local array (local_in).

据我了解OpenCL工作组/工作项,我需要做的是将全局16x16 ROI从全局内存复制到本地内存。 (如果我错了,请纠正我,因为我从此开始)。

因此,在屏障之后,local_in中的每个元素都可以通过wi_c + wi_r*local_size进行访问。

但现在让我们做一些棘手的事情。如果我希望我的工作组中的每个工作项都能在3x3邻域上工作,那么我需要一个18x18的local_in数组。

但是如何创造呢?因为我只有16x16 = 256个工作项(线程),但我需要18x18 = 324(缺少68个线程来做)。

我的基本想法应该是:

if(wi_c == 0 && wi_r == 0){
    // Code that copy the border into the new array that should be
    // local_in[(local_size+2)*(local_size+2)];         
}

但这很糟糕,因为第一个工作项目(第一个线程)将必须处理所有边界,而该组中的其余工作项目将只等待第一个工作项目完成。 (再次,这是我对OpenCL的理解,可能是错误的。)

所以这是我真正的问题:

  1. 是否有另一种更容易解决此类问题的方法?比如将NDRange本地大小更改为重叠还是什么?
  2. 我开始阅读有关合并内存访问的内容,是我的第一个代码草案看起来像吗?我不这么认为,因为我正在使用" stride"加载全局内存的方法。但我不明白如何将代码的第一部分改为高效。
  3. 一旦达到障碍,则继续处理每个工作项以获得需要存储回全局输出数组的最终值。我应该在此之前再次设置一个障碍"写"还是一切都好,让所有的工作项目都能完成自我?

1 个答案:

答案 0 :(得分:0)

我尝试了不同的方法,但我选择了最终版本,而不是#34;如果"并尽可能地使用线程(在第二阶段,可能不是完全有效的,因为很少有线程空闲,但它是我能够获得的最好的)。

原理是在左上角设置原点(起始位置),并使用循环索引从该位置创建读/写索引。循环从2D中的本地id位置开始。所以所有256个工作项都写出了他们的第一个元素,而在第二个阶段,只有68个工作项在256上完成了2个底行+2个右列。

我还不是OpenCL专家,所以这仍然可以有更多改进(可能循环展开,我不知道)。

    __local float wrkSrc[324];
    const int lpitch = 18;

    // Add halfROI to handle the corner
    const int lcol = get_local_id(0);
    const int lrow = get_local_id(1);

    const int2 gid = { col, row };
    const int2 lid = { lcol, lrow };

    // Always get the most Top-left corner of that ROI to extract.
    const int2 startPos = gid - lid - halfROI;

    // Loop on each thread to get their right ID.
    // Thread with id < 2 * halfROI will process more then others, but not that much an issue.
    for ( int x = lid.x; x < lpitch; x += 16 ) {
        for ( int y = lid.y; y < lpitch; y += 16 ) {

            // Get the position to write into the local array.
            const int lidx = x + y * lpitch;

            // Get the position to read into the global memory (src)
            const int2 readPos = startPos + (int2)( x, y );

            // Is inside ?
            if ( readPos.x >= 0 && readPos.x < width && readPos.y >= 0 && readPos.y < height )
                wrkSrc[lidx] = src[readPos.x + readPos.y * lab_new_pitch];
            else
                wrkSrc[lidx] = 0.0f;
        }
    }