OpenMP 并行用于 - 多个并行用于的 Vs。一个并行,其中包含多个 for

Posted

技术标签:

【中文标题】OpenMP 并行用于 - 多个并行用于的 Vs。一个并行,其中包含多个 for【英文标题】:OpenMP parallel for -- Multiple parallel for's Vs. one parallel that includes within it multiple for's 【发布时间】:2021-08-11 06:12:18 【问题描述】:

我正在通过Using OpenMP。作者比较和对比了以下两种结构:

//Construct 1
#pragma omp parallel for
for( ... )

    /* Work sharing loop 1 */

...
#pragma omp parallel for
for( ... )

    /* Work sharing loop N */

反对

//Construct 2
#pragma omp parallel

    #pragma omp for    
    for( ... )
    
        /* Work sharing loop 1 */
    
    ...
    #pragma omp for    
    for( ... )
    
        /* Work sharing loop N */
    

他们说构造 2

隐含的障碍较少,并且可能存在缓存 循环之间的数据重用。这种方法的缺点是一个 不能再在每个循环的基础上调整线程数,但是 这通常不是真正的限制。

我很难理解 Construct 2 如何具有更少的隐含障碍。由于#pragma omp for,在每个 for 循环之后,构造 2 中是否没有隐含的障碍?那么,在每种情况下,隐含障碍的数量是否都相同,N?也就是Construct 2中不是先出现第一个循环,以此类推,然后最后执行Nth for循环吗?

另外,Construct 2 如何更利于循环之间的缓存重用?

【问题讨论】:

【参考方案1】:

我很难理解 Construct 2 如何减少 隐含的障碍。在 Construct 2 之后是否没有隐含的障碍? 每个 for 循环由于#pragma omp for?所以,在每种情况下,不是 隐含障碍的数量相同,N?也就是说,在 构造 2,第一个循环首先发生,依此类推,然后 最后执行第 N 个 for 循环?

我没有读过这本书,但根据您所展示的内容,实际上是相反的,即:

 //Construct 1
#pragma omp parallel for
for( ... )

    /* Work sharing loop 1 */
 // <-- implicit barrier 
...
#pragma omp parallel for
for( ... )

    /* Work sharing loop N */
 // <-- implicit barrier.

N个隐含障碍(在每个平行区域的末尾),而第二个代码:

 //Construct 2
#pragma omp parallel

    #pragma omp for    
    for( ... )
    
        /* Work sharing loop 1 */
     <-- implicit barrier
    ...
    #pragma omp for    
    for( ... )
    
        /* Work sharing loop N */
     <-- implicit barrier
 <-- implicit barrier

有 N+1 个障碍(在每个 for + 平行区域的末尾。

实际上,在这种情况下,由于最后两个隐式障碍之间没有计算,因此可以将nowait 添加到最后一个#pragma omp for 以消除其中一个冗余障碍。

第二个代码比第二个代码具有更少隐式障碍的一种方法是在#pragma omp for 子句中添加一个nowait 子句。

从您所展示的书的链接中:

最后,使用 OpenMP 考虑了可能影响 OpenMP 的趋势 发展,提供对未来可能性的一瞥 OpenMP 3.0 从当前 OpenMP 2.5 的优势来看。 多核电脑的使用越来越多,需要一个全面的 标准接口的介绍和概述清晰。

所以这本书使用的是old OpenMP 2.5 标准,从standard 关于loop 构造函数的内容我们可以读到:

在循环构造函数的末尾有一个隐式屏障 除非指定了 nowait 子句。

nowait 不能添加到 parallel 构造函数,但可以添加到 for 构造函数。因此,如果可以将nowait 子句添加到#pragma omp for 子句,则第二个代码可能 具有更少的隐式障碍。然而,事实上,第二个代码实际上比第一个代码具有更多隐含的障碍。

另外,Construct 2 如何更利于循环之间的缓存重用?

如果您在线程之间使用循环迭代的static 分布(例如, 第二个代码中的#pragma omp for scheduler(static, ...),相同的线程将使用相同的循环迭代。例如, 有两个线程,让我们称它们为 Thread AThread B。如果我们假设具有 chunk=1Thread AB静态 分布将适用于奇数和偶数迭代每个循环,分别。因此,根据实际的应用程序代码,这可能意味着这些线程将使用给定数据结构的相同内存位置(例如,相同的数组位置)。 p>

在第一个代码中,理论上(但这将取决于具体的 OpenMP 实现),因为有两个不同的并行区域,不同的线程可以在两个循环之间选择相同的循环迭代.换句话说,在我们使用两个线程的示例中,不能保证在一个循环中计算偶数(或奇数)的同一线程会在其他循环中计算相同的数字。

【讨论】:

以上是关于OpenMP 并行用于 - 多个并行用于的 Vs。一个并行,其中包含多个 for的主要内容,如果未能解决你的问题,请参考以下文章

openMP:并行运行所有线程会导致内存不足异常

哪个库用于迭代 1M*1k 次的并行 for 循环,OpenMP 或 boost::thread?

OpenMP 中的 C++ 动态内存分配速度较慢,即使对于非并行代码段也是如此

OpenMp实现并行化

OpenMP用法大全

OpenMP基本概念转