c - section 和 task openmp 的区别

标签 c parallel-processing openmp

OpenMP 之间有什么区别:

#pragma omp parallel sections
{
    #pragma omp section
    {
       fct1();
    }
    #pragma omp section
    {
       fct2();
    }
}

和:

#pragma omp parallel 
{
    #pragma omp single
    {
       #pragma omp task
       fct1();
       #pragma omp task
       fct2();
    }
}

我不确定第二个代码是否正确...

最佳答案

任务和部分之间的区别在于代码执行的时间范围。部分包含在 sections 构造中,并且(除非指定了 nowait 子句)线程将不会离开它,直到所有部分都已执行:

                 [    sections     ]
Thread 0: -------< section 1 >---->*------
Thread 1: -------< section 2      >*------
Thread 2: ------------------------>*------
...                                *
Thread N-1: ---------------------->*------

这里 N 线程遇到一个包含两个部分的 sections 结构,第二个比第一个花费更多的时间。前两个线程各执行一个部分。其他 N-2 线程只是在 sections 构造末尾的隐式屏障处等待(此处显示为 *)。

只要有可能,任务就会在所谓的任务调度点排队并执行。在某些情况下,可以允许运行时在线程之间移动任务,即使是在它们的生命周期中期。此类任务称为未绑定(bind)任务,未绑定(bind)任务可能会在一个线程中开始执行,然后在某个调度点,它可能会被运行时迁移到另一个线程。

尽管如此,任务和部分在很多方面还是相似的。例如,以下两个代码片段实现了基本相同的结果:

// sections
...
#pragma omp sections
{
   #pragma omp section
   foo();
   #pragma omp section
   bar();
}
...

// tasks
...
#pragma omp single nowait
{
   #pragma omp task
   foo();
   #pragma omp task
   bar();
}
#pragma omp taskwait
...

taskwait 的工作方式与 barrier 非常相似,但对于任务 - 它确保当前执行流程将暂停,直到所有排队的任务都已执行。它是一个调度点,即它允许线程处理任务。需要 single 构造,这样任务将仅由一个线程创建。如果没有 single 构造,每个任务将被创建 num_threads 次,这可能不是人们想要的。 single 构造中的 nowait 子句指示其他线程不要等到 single 构造被执行(即在最后删除隐式屏障single 构造)。所以他们立即点击 taskwait 并开始处理任务。

taskwait 是一个明确的调度点,为清楚起见,此处显示。还有隐式调度点,最显着的是在障碍同步内,无论是显式还是隐式。因此,上面的代码也可以简单的写成:

// tasks
...
#pragma omp single
{
   #pragma omp task
   foo();
   #pragma omp task
   bar();
}
...

这是如果存在三个线程可能会发生的一种可能情况:

               +--+-->[ task queue ]--+
               |  |                   |
               |  |       +-----------+
               |  |       |
Thread 0: --< single >-|  v  |-----
Thread 1: -------->|< foo() >|-----
Thread 2: -------->|< bar() >|-----

中显示这里| ... | 是调度点的操作(taskwait 指令或隐式屏障)。基本上,线程 12 暂停它们此时正在做的事情,并开始处理队列中的任务。处理完所有任务后,线程将恢复其正常执行流程。请注意,线程 12 可能会在线程 0 退出 single 构造之前到达调度点,因此左边的 | 不需要对齐(如上图所示)。

也可能发生线程 1 能够完成处理 foo() 任务并请求另一个任务,甚至在其他线程能够请求任务之前。所以 foo()bar() 可能会被同一个线程执行:

               +--+-->[ task queue ]--+
               |  |                   |
               |  |      +------------+
               |  |      |
Thread 0: --< single >-| v             |---
Thread 1: --------->|< foo() >< bar() >|---
Thread 2: --------------------->|      |---

如果线程 2 来得太晚,被挑出的线程也有可能执行第二个任务:

               +--+-->[ task queue ]--+
               |  |                   |
               |  |      +------------+
               |  |      |
Thread 0: --< single >-| v < bar() >|---
Thread 1: --------->|< foo() >      |---
Thread 2: ----------------->|       |---

在某些情况下,编译器或 OpenMP 运行时甚至可能完全绕过任务队列并串行执行任务:

Thread 0: --< single: foo(); bar() >*---
Thread 1: ------------------------->*---
Thread 2: ------------------------->*---

如果区域代码中没有任务调度点,OpenMP 运行时可能会在它认为合适的时候启动任务。例如,所有任务都可能被推迟,直到到达 parallel 区域末尾的障碍。

关于c - section 和 task openmp 的区别,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13788638/

相关文章:

c - 有什么建议或改进吗?

c++ - OpenMP parallel for with vector of vectors

c - 如何使用指针调用结构体内部的结构体?

c - 使用 pthread_join 的 pthread 同步问题

Python 中的 C# Parallel.Foreach 等价物

python - 在 Python 中使用 API 调用运行需要很长时间的脚本的最佳方法

c - 如何使用 openmp 在 c 中进行具有一些 for 循环的任务并行化?

c - block 中的 OpenMP 动态循环分解

c - 在 C 中用指针分配结构

c - libxml2:不单独报告 ' or "等字符