在OpenMP中设计具有任务的算法

Designing an algorithm with tasks in openmp

本文关键字:任务 算法 OpenMP      更新时间:2023-10-16

i当前有一个代码,该代码确实可以在大型数据集上工作,并在末尾生成一个数组。然后,必须将数组添加到全局缓冲区中,并且所有这些都发生在循环中。例如

for(i=0;i<10000;i++)
{   <1. do some processing, generate a 1M-sized array>
    <2. update this array into global buffer>
}

任务1发生在GPU上,我希望第二部分在CPU上类似地发生,即1线程控制任务1的GPU,所有其他线程在1个缓冲区准备处理时都会执行任务2。对于基本情况,我该如何使用一个缓冲区的副本(即在GPU侧,一个在CPU侧面进行GPU副本)?

您可以做的是拥有一组生产者 - 消费者,以便步骤1产生一个缓冲区,而步骤2在将结果纳入您的全局数组时会消耗该缓冲区。

类似(通常parallel single):

const int buffer_size = 100000;
type global[buffer_size];
type buffer[2][buffer_size];
for(int i=0;i<10000;i++)
{
    type *buf[buffer_size] = buffer[i % 2];
    #pragma omp task depend(out: [buffer_size]buf)
    {
        <1. write to buf>
    }
    #pragma omp task depend(in: [buffer_size]buf) depend(inout: [buffer_size]global)
    {
        <2. update global using buf>
    }
}

这允许步骤2与步骤1重叠,其中一个迭代的"滞后"是迭代i = 1的步骤1,将同时运行到步骤2的迭代i = 0。您将需要每个重叠迭代的缓冲区,以便每个同时操作可以彼此独立进行。

现在,BUF的语义(in/out)很简单,因为它们是简单的Procuder-Consumbers关系。对于全局,您需要inout,当您更新"就地的缓冲区:获取当前数组(in)并为其分配新值(out)。这意味着步骤2的所有任务将由于这种依赖性而被序列化,这意味着最多可以重叠的2个迭代(并且使用超过2个缓冲区都没有意义)。

这是当前OpenMP(4.5)任务的限制,尽管存在解决此问题的任务减少的建议。另一种方法是将Global声明为shared,在这种情况下,您需要使用原子操作进行修改。

您可以使用target子句在GPU上执行任务1。确保将来自卸载设备(即GPU)的数据复制到CPU。在步骤2的任务中,您可以使用平行于构造或任务的步骤2本身并行化步骤2本身。