Optimization 在OpenCL中构造偏移邻域上的操作的更快方法

Optimization 在OpenCL中构造偏移邻域上的操作的更快方法,optimization,opencl,gpgpu,pyopencl,Optimization,Opencl,Gpgpu,Pyopencl,在OpenCL中,如何构造2D数组中多个重叠但偏移的块上的操作,以便更高效地执行? 例如,我有以下OpenCL内核: __kernel void test_kernel( read_only image2d_t src, write_only image2d_t dest, const int width, const int height ) { const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE

在OpenCL中,如何构造2D数组中多个重叠但偏移的块上的操作,以便更高效地执行?

例如,我有以下OpenCL内核:

__kernel void test_kernel(
    read_only image2d_t src,
    write_only image2d_t dest,
    const int width,
    const int height
)
{
    const sampler_t sampler =  CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_NEAREST;
    int2 pos = (int2)(get_global_id(0), get_global_id(1));
    int2 pos0 = (int2)(pos.x - pos.x % 16, pos.y - pos.y % 16);

    uint4 diff = (uint4)(0, 0, 0, 0);

    for (int i=0; i<16; i++)
    {
        for (int j=0; j<16; j++)
        {
            diff += read_imageui(src, sampler, (int2)(pos0.x + i, pos0.y + j)) -
                read_imageui(src, sampler, (int2)(pos.x + i, pos.y + j));
        }
    }
    write_imageui(dest, pos, diff);
}

编辑:考虑到内存访问模式,原始版本可能相当不错;调用
read_-imageui(src,sampler,(int2)(pos0.x+i,pos0.y+j))
时,工作组中的所有工作项都在读取相同的位置(因此这只是一次读取???),调用
read_-imageui(src,sampler,(int2)(pos.x+i,pos.y+j))
时,它们正在读取顺序位置(因此可以完美地合并读取??).

这肯定是内存访问问题。相邻工作项的像素可以重叠多达15x16,更糟糕的是,每个工作项将重叠至少225个其他工作项

我会使用本地内存,让工作组协作处理许多16x16块。我喜欢为每个工作组使用一个大的方形块。矩形块有点复杂,但可以为您获得更好的内存利用率

如果从源图像中读取n×n像素的块,边界将重叠nx15(或15xn)。您需要根据可用的本地内存大小(LDS)计算n的最大可能值。如果您使用的是OpenCL1.1或更高版本,LDS至少为32kb。OpenCL1.0承诺每个工作组16kb

n <= sqrt(32kb / sizeof(uint4))
n <= sqrt(32768 / 16)
n ~ 45
n一些建议:


  • 在每个工作项中计算1个以上的输出像素。它将增加数据重用
  • 对不同的工作组大小进行基准测试,以最大限度地利用纹理缓存
  • 也许有一种方法可以将内核分为两个过程(水平和垂直)
更新:更多建议

不要在本地内存中加载所有内容,尝试只加载本地\u src值,并对另一个值使用read\u image


由于您几乎不进行任何计算,因此应以GB/s为单位测量读取速度,并与峰值内存速度进行比较。

“每个工作项中有超过1个输出像素”-我认为这将破坏任何合并内存访问的能力。“不同的工作组大小”-尝试过,16x16可能是最大的,8x32工作(相同的速度),32x8不工作,8x8较慢。“将内核分为两个过程”-唉,不是。读取速度:如果工作项之间不共享内存访问,那么内核读取~2GB。我试着按照你的建议去做,看看,这是你的想法吗?如果只复制不偏移的数据(每个工作项相同),速度会快一点,如果复制偏移的数据,速度会慢很多。我正在考虑一个大的本地块,其中包含许多工作组所需的数据。16x16仍然太小。我会尽快为您发布一些代码。谢谢您提供更多详细信息。我想(?)我完全按照你在我上面发布的第二个内核中的建议做了,local_src2和你的buff一样是32x32。不幸的是,它并没有加快速度。你能发现我需要更改的地方吗?如果是这样的话,你可能想把“读取顺序位置”改为“读取相邻位置”(工作组中的工作项读取相邻位置)。这就是合并阅读的方式,而不是按顺序操作。您希望工作项读取相邻的内存位置,以便可以将读取合并为一个宽的读取。
n <= sqrt(32kb / sizeof(uint4))
n <= sqrt(32768 / 16)
n ~ 45