OpenCL-工作组轴是否可交换?

OpenCL-工作组轴是否可交换?,opencl,Opencl,我试图为一个问题找到最佳的工作组规模,我发现了一些我自己无法证明的事情 以下是我的结果: GlobalWorkSize{6400 6400 1},WorkGroupSize{64 4 1},时间(毫秒)=44.18 GlobalWorkSize{6400 6400 1},WorkGroupSize{4 64 1},时间(毫秒)=24.39 交换轴会使执行速度提高两倍。为什么 顺便说一下,我用的是AMD的GPU 谢谢:-) 编辑: 这是内核(一个简单的矩阵变换): 我同意@Thomas,这很可

我试图为一个问题找到最佳的工作组规模,我发现了一些我自己无法证明的事情

以下是我的结果:

  • GlobalWorkSize{6400 6400 1},WorkGroupSize{64 4 1},时间(毫秒)=44.18
  • GlobalWorkSize{6400 6400 1},WorkGroupSize{4 64 1},时间(毫秒)=24.39
交换轴会使执行速度提高两倍。为什么

顺便说一下,我用的是AMD的GPU

谢谢:-)

编辑: 这是内核(一个简单的矩阵变换):


我同意@Thomas,这很可能取决于您的内核。最可能的情况是,在第二种情况下,您以合并方式访问内存和/或充分利用内存事务

合并:当线程需要访问内存中的元素时,硬件会尝试以尽可能少的事务访问这些元素,即如果线程0和线程1必须访问连续元素,则只有一个事务

内存事务的充分利用:假设您有一个GPU在一个事务中获取32字节。因此,如果您有4个线程,每个线程需要获取一个int,那么您只使用事务获取的数据的一半;剩下的就浪费掉了(假设int为4字节)

为了说明这一点,假设您要访问一个n×n矩阵。你的矩阵是在行主要,你使用n个线程组织在一个维度。你有两种可能:

  • 每个工作项负责一个列,一次循环一个列元素
  • 每个工作项负责一行,一次循环一个行元素
  • 这可能违反直觉,但第一种解决方案将能够进行合并访问,而第二种解决方案则不能。原因是当第一个工作项需要访问第一列中的第一个元素时,第二个工作项将访问第二列中的第一个元素,依此类推。这些元素在内存中是连续的。第二种解决方案并非如此

    现在,如果您使用相同的示例,并应用解决方案1,但是这次您有4个工作项而不是n,并且使用我刚才提到的相同GPU,您很可能会将时间增加一倍2,因为您将浪费一半的内存事务

    编辑:既然你发布了你的内核,我发现我忘了提到其他东西

    对于内核,选择(1256)或(256,1)的本地大小似乎总是一个错误的选择。在第一种情况下,在输入中读取一列需要256个事务(每个事务获取32个字节,其中只有4个将被使用-记住与我前面的示例相同的GPU),而在输出中写入32个事务则需要256个事务:您可以在一个事务中写入8个浮点,因此32个事务将写入256个元素

    这与工作组大小为(256,1)时的问题相同,但这次使用32个事务进行读取,使用256个事务进行写入

    那么为什么第一种尺寸的效果更好呢?这是因为有一个缓存系统,可以减轻读取部分的错误访问。因此,大小(1256)适合写部分,缓存系统处理不太好的读部分,从而减少必要的读事务数

    请注意,事务的数量总体上减少了(考虑到NDRange中的所有工作组)。例如,第一个工作组发出256个事务,以读取第一列的前256个元素。第二个工作组可能只是进入缓存中检索第二列的元素,因为它们是由第一个工作组发出的事务(32字节)获取的


    现在,我几乎可以肯定,您可以做得比(1256)try(8,32)更好。

    我想这取决于内核;]你能把内核寄出去吗?这是一个如何通过计算或内存操作进行迭代的问题。这是我用来理解你提到的东西的内核(矩阵转置):'内核无效转置('全局浮点输入,'全局浮点*输出,常量int size){int I=get_全局id(0);int j=get_全局id(1);输出[isize+j]=input[j*size+i];}但我得到的结果并不是我所期望的。对于二维范围,我得到了18.59毫秒的“全局[0]=6400;全局[1]=6400;局部[0]=1;局部[1]=256;”和146.107毫秒的“全局[0]=6400;全局[1]=6400;局部[0]=256;局部[1]=1;”。我得到了错误的结果还是不明白你的解释?
    __kernel void transpose(__global float *input, __global float *output, const int size){
        int i = get_global_id(0);
        int j = get_global_id(1);
        output[i*size + j] = input[j*size + i];
    }