在单个数组中与工作组及其大小合作

Working with work groups and their sizes in opencl on a single array

本文关键字:工作组 单个 数组      更新时间:2023-10-16

我正在使用opencl c 进行项目的实现。我想从GPU/S中获得最大速度/性能(取决于我有多个GPU还是一个GPU)。但是出于这个问题的目的,让我们假设我只有一个设备。

假设我的长度为100。

double arr[100];

现在我目前正在做的是我通过以下方法调用内核。

kernelGA(cl::EnqueueArgs(queue[iter],
                         cl::NDRange(100)),
                         d_arr, // and some other buffers.
         )

现在在内核一侧。我有一个全球ID。那就是:

int idx = get_global_id(0);

我想要我的内核的工作方式如下:

  • 100个工作组中的每个人将每个元素都照顾一个元素。
  • 使用一些规则,每个工作组都在更新数组的元素。例如:

    if (arr[idx] < 5) {
        arr[idx] = 10; // a very simple example.
    }
    

对于大多数零件,都可以。但是,我想互换一个点,以及我希望线程/工作项目相互通信的地方。到那时,它们似乎不起作用,并且似乎没有交流。

eg:

if(arr[idx] < someNumber) {
    arr[idx] = arr[idx + 1];
}

在这一点上,似乎没有任何作用。我试图实现循环并创建障碍

barrier(CLK_LOCAL_MEM_FENCE | CLK_GLOBAL_MEM_FENCE);

,但也行不通。它不会更改数组元素的值。

我有以下问题:

1。为什么不起作用?我的实施错误吗?这些线程似乎正确更新了自己的索引数组元素。但是,当他们之间的交流时,它们不起作用。为什么?

2。我的障碍实现,只让一个工作项目错误吗?有没有更好的方法让一个项目在等待此部分完成此部分?

您编写的代码是序列的:

if(arr[idx] < someNumber) {
   arr[idx] = arr[idx + 1];
}

工人n将取得n-1,n-1的结果,n-2的结果,依此类推。因此,这意味着工人n需要等待所有其他人完成。这意味着代码不平行,永远不会。使用CPU比GPU要好得多。

OpenCL设计模型,允许您并行运行多个工作项目,但是同步模型仅允许在工作组内同步。如果您需要全局同步,则明确表明您的算法不适合Opencl。


现在,如果我假设您只需要最后一个元素的值。您真正想要的是执行所有数组的"总和"。然后,这是一个减少问题,可以通过这种方式并行化在日志(n)时间(n)时间执行它:

1st step,   array[x] = array[x] + array[N/2+x] (x from 0 to N/2)
2nd step,   array[x] = array[x] + array[N/4+x] (x from 0 to N/4)
...
log(N) passes

每个步骤将是一个单独的内核,因此确保所有工作项目在开始下一批之前已经完成。

另一个更快的选项是在工作组内进行减少,因此,如果工作组的大小为256,则可以在每次通过中共同组合256。

比仅减少2个要快2

我怀疑您的问题代表了一个并行能力有限的问题,因此适合任何类型的GPGPU解决方案。

考虑以下元素数组:

1 5 2 6 5 3 6 7 2 8 1 8 3 4 2

现在假设我们对此数据执行以下转换:

//A straightforward, serially-executed iteration on the entire array.
for(int i = 0; i < arr.size() - 1; i++) {
    if(arr[i] < 5) arr[i] = arr[i + 1];
}

结果将为

5 5 6 6 5 6 6 7 8 8 8 8 4 2 2

但是,如果for循环反向执行会发生什么?

for(int i = arr.size() - 2; i >= 0; i--) {
    if(arr[i] < 5) arr[i] = arr[i + 1];
}

结果将为

5 5 6 6 5 6 6 7 8 8 8 8 2 2 2

请注意,根据执行顺序,第三到持久的数字有何不同。我的示例输入不会发生太大的变化,但是如果您的代码在所选阈值以下的数字大多数,则可以完全更改整个数组!因为GPGPU API不能保证单个工作项目的执行顺序,这意味着您的执行顺序可以像我写的第一个循环的for 一样,或者它可以可以就像我写的第二个循环一样,或者可以是一个完全随机的订单 - 您已经编写了非确定性代码,而使其确定性的唯一方法是使用SO SO SO SO SO您要保证顺序排序的许多障碍,在这一点上,没有理由首先使用GPGPU API。

您可以写下类似以下内容,这是确定性的:

if(arr[i] < 5) output[i] = arr[i + 1];
else output[i] = arr[i];

,但这可能需要重新考虑您的设计约束。我不知道,因为我不知道您的程序最终会在做什么。

无论哪种方式,您都需要花一些时间重新考虑您实际尝试做的事情。