OpenMP任务有和没有并行

Ann*_*Kim 7 task openmp

Node *head = &node1;
while (head)
{
    #pragma omp task 
        cout<<head->value<<endl;
    head = head->next;
}

#pragma omp parallel
{
    #pragma omp single
    {
        Node *head = &node1;
        while (head)
        {
            #pragma omp task 
                cout<<head->value<<endl;
            head = head->next;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在第一个块中,我只创建了没有并行指令的任务,而在第二个块中,我使用了并行指令和单指令,这是我在论文中看到的常见方法.我想知道它们之间的区别是什么?顺便说一句,我知道这些指令的基本含义.

我的评论中的代码:

void traverse(node *root)
{
    if (root->left) 
    {
        #pragma omp task 
        traverse(root->left);
    }
    if (root->right) 
    {
        #pragma omp task 
        traverse(root->right);
    }
    process(root);
}
Run Code Online (Sandbox Code Playgroud)

Hri*_*iev 13

区别在于,在第一个块中,您并没有真正创建任何任务,因为块本身不是在活动并行区域内嵌套(在语法上也不是词法上).在第二个块中,task构造在语法中嵌套在parallel区域内,如果区域在运行时恰好处于活动状态,则构造将显式任务(活动并行区域是由多个线程的团队执行的区域).词汇嵌套不太明显.请注意以下示例:

void foo(void)
{
   int i;

   for (i = 0; i < 10; i++)
      #pragma omp task
      bar();
}

int main(void)
{
   foo();

   #pragma omp parallel num_threads(4)
   {
      #pragma omp single
      foo();
   }

   return 0;
}
Run Code Online (Sandbox Code Playgroud)

第一次调用foo()发生在任何并行区域之外.因此,该task指令(几乎)没有任何内容,所有调用bar()都是串行发生的.第二次调用foo()来自并行区域内部,因此将在内部生成新任务foo().该parallel区域处于活动状态,因为4num_threads(4)子句固定了线程数.

OpenMP指令的这种不同行为是一种设计特征.主要思想是能够编写可以作为串行和并行执行的代码.

仍然存在task构造进行foo()一些代码转换,例如foo()转换为类似的东西:

void foo_omp_fn_1(void *omp_data)
{
   bar();
}

void foo(void)
{
   int i;

   for (i = 0; i < 10; i++)
      OMP_make_task(foo_omp_fn_1, NULL);
}
Run Code Online (Sandbox Code Playgroud)

以下OMP_make_task()是来自OpenMP支持库的假设(非公开可用)函数,该函数将对函数的调用排队,作为其第一个参数提供.如果OMP_make_task()检测到它在活动的并行区域之外工作,则只需调用它foo_omp_fn_1().这bar()为串行情况下的调用增加了一些开销.而不是main -> foo -> bar,电话就像main -> foo -> OMP_make_task -> foo_omp_fn_1 -> bar.这意味着串行代码执行速度较慢.

使用工作共享指令更清楚地说明了这一点:

void foo(void)
{
   int i;

   #pragma omp for
   for (i = 0; i < 12; i++)
      bar();
}

int main(void)
{
   foo();

   #pragma omp parallel num_threads(4)
   {
      foo();
   }

   return 0;
}
Run Code Online (Sandbox Code Playgroud)

第一次调用foo()将以串行方式运行循环.第二次调用将在4个线程中分配12次迭代,即每个线程只执行3个iteratons.再一次,使用一些代码转换魔术来实现这一点,并且串行循环运行速度比不#pragma omp for存在时慢foo().

这里的教训是永远不要在不必要的地方添加OpenMP结构.