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

时间:2013-07-31 03:56:22

标签: opencl

我试图找到问题的最佳工作组大小,我想出了一些我无法为自己辩护的事情。

这些是我的结果:

  • GlobalWorkSize {6400 6400 1},WorkGroupSize {64 4 1},时间(毫秒)= 44.18
  • GlobalWorkSize {6400 6400 1},WorkGroupSize {4 64 1},时间(毫秒)= 24.39

交换轴导致执行速度提高两倍。为什么!?

顺便说一句,我使用的是AMD GPU。

谢谢: - )

编辑: 这是内核(简单矩阵转换):

__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];
}

1 个答案:

答案 0 :(得分:3)

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

合并:当线程需要访问内存中的元素时,硬件会尝试尽可能少地访问这些元素,即如果线程0和线程1必须访问连续元素,那么只是一笔交易。

完全使用内存事务:假设你有一个GPU在一个事务中获取32个字节。因此,如果您有4个线程需要获取一个int,那么您只使用事务获取的一半数据;你浪费了其余的时间(假设int是4个字节)。

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

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

    现在,如果你采用相同的例子,并应用解决方案1,但这次你有4个工作项而不是n和我刚刚说过的相同的GPU,你很可能会将时间增加2倍,因为你会浪费你一半的记忆交易。

    编辑:现在您发布了内核,我发现我忘记提及其他内容了。

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

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

    那么为什么第一种尺寸更好?这是因为有一个缓存系统,可以减轻读取部分的错误访问。因此,大小(1,256)对于写入部分是有利的,并且高速缓存系统处理不太好的读取部分,减少了必要的读取事务的数量。

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

    现在,我几乎可以肯定你可以比(1,256)尝试做得更好(8,32)。