首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >OpenMP:同一编译指示上的nowait和reduction子句

OpenMP:同一编译指示上的nowait和reduction子句
EN

Stack Overflow用户
提问于 2011-06-11 20:27:48
回答 3查看 13.9K关注 0票数 10

我正在研究OpenMP,并遇到了以下示例:

代码语言:javascript
复制
#pragma omp parallel shared(n,a,b,c,d,sum) private(i)
{
    #pragma omp for nowait
    for (i=0; i<n; i++)
        a[i] += b[i];

    #pragma omp for nowait
    for (i=0; i<n; i++)
        c[i] += d[i];
    #pragma omp barrier

    #pragma omp for nowait reduction(+:sum)
    for (i=0; i<n; i++)
        sum += a[i] + c[i];
} /*-- End of parallel region --*/

在最后一个for循环中,有一个nowait和一个clause子句。这是正确的吗?reduction子句不需要同步吗?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2011-06-11 21:35:26

第二个也是最后一个循环中的nowait有点多余。OpenMP规范在区域结束之前提到了nowait,所以也许这可以保留下来。

但是第二个循环之前的nowait和它之后的显式屏障相互抵消。

最后,关于sharedprivate子句。在代码中,shared没有任何作用,根本不应该使用private:如果需要线程私有变量,只需在并行区域中声明它即可。特别是,您应该在循环中声明循环变量,而不是在循环之前。

要使shared有用,您需要告诉OpenMP默认情况下它不应该共享任何内容。您应该这样做,以避免由于意外共享变量而导致的错误。这可以通过指定default(none)来完成。这就给我们留下了:

代码语言:javascript
复制
#pragma omp parallel default(none) shared(n, a, b, c, d, sum)
{
    #pragma omp for nowait
    for (int i = 0; i < n; ++i)
        a[i] += b[i];

    #pragma omp for
    for (int i = 0; i < n; ++i)
        c[i] += d[i];

    #pragma omp for nowait reduction(+:sum)
    for (int i = 0; i < n; ++i)
        sum += a[i] + c[i];
} // End of parallel region
票数 19
EN

Stack Overflow用户

发布于 2011-06-13 02:45:23

在某些方面,这似乎是一个家庭作业问题,我讨厌为人们做这件事。另一方面,上面的答案并不完全准确,我觉得应该改正。

首先,虽然在本例中不需要shared和private子句,但我不同意Konrad的观点,即不应该使用它们。人们并行化代码最常见的问题之一是,他们没有花时间去理解变量是如何被使用的。没有私有化和/或保护应该私有化的共享变量,这是我看到的最大数量的问题。通过检查变量是如何使用的,并将它们放入适当的共享、私有等子句中,将大大减少您遇到的问题的数量。

至于障碍的问题,第一个循环可以有一个nowait子句,因为在第二个循环中没有使用计算的值(a)。仅当在计算值之前未使用值computed (c)时,第二个循环才能具有nowait子句(即,没有依赖关系)。在原始示例代码中,在第二个循环上有一个nowait,但在第三个循环之前有一个显式的障碍。这很好,因为您的教授试图展示显式屏障的使用-尽管在第二个循环中省略nowait会使显式屏障变得多余(因为在循环的末尾有一个隐式屏障)。

另一方面,第二个循环上的nowait和显式屏障可能根本不需要。在OpenMP V3.0规范之前,许多人认为规范中没有澄清的事情是正确的。对于OpenMP V3.0规范,将以下内容添加到2.5.1节循环结构,表2-1调度子句类型值,静态(调度):

如果满足以下条件,符合静态调度的实现必须确保在两个循环区域中使用相同的逻辑迭代编号分配: 1)两个循环区域具有相同的循环迭代次数,2)两个循环区域指定相同的chunk_size值,或者两个循环区域没有指定chunk_size,以及3)两个循环区域绑定到相同的并行区域。保证满足两个这样的循环中的相同逻辑迭代之间的数据相关性,从而允许安全地使用nowait子句(有关示例,请参见第170页的A.9节)。

现在,在您的示例中,任何循环上都没有显示时间表,因此这可能适用也可能不适用。原因是,默认计划是实现定义的,虽然目前大多数实现将默认计划定义为静态,但不能保证这一点。如果你的教授在所有三个循环上都设置了一个没有块大小的静态调度类型,那么可以在第一个和第二个循环上使用nowait,并且在第二个和第三个循环之间根本不需要任何屏障(无论是隐式的还是显式的)。

现在我们进入第三个循环,你的问题是nowait和reduction。正如Michy所指出的,OpenMP规范允许同时指定(reduction和nowait)。然而,不需要同步就能完成缩减的说法是不正确的。在本例中,隐式屏障(在第三个循环的末尾)可以使用nowait移除。这是因为在遇到并行区域的隐式屏障之前没有使用缩减(sum)。

如果您查看OpenMP V3.0规范的2.9.3.6节缩减条款,您将发现以下内容:

如果不使用nowait,则缩减计算将在构造结束时完成;但是,如果在也应用了nowait的构造上使用reduction子句,则对原始列表项的访问将创建竞争,因此具有未指定的效果,除非同步确保它们在所有线程执行完所有迭代或区段构造之后发生,并且缩减计算已经完成并存储了该列表项的计算值。这可以通过屏障同步得到最简单的保证。

这意味着,如果您想在第三次循环之后在并行区域中使用sum变量,那么在使用它之前,您需要一个屏障(隐式或显式)。正如现在的示例所示,它是正确的。

票数 9
EN

Stack Overflow用户

发布于 2011-06-11 21:24:58

OpenMP speficication说:

循环构造的语法如下:

#杂注omp for [子句[,子句] ... ]新行for-循环

where子句是以下内容之一:

..。归约(运算符:列表) ...nowait

所以可以有更多的子句,因此可以同时有reduction和nowait语句。

reduction子句中不需要显式同步-添加到sum变量的操作是同步的,因为reduction(+: sum)和以前的屏障强制abreduction循环时具有最终值。nowait意味着如果线程完成了循环中的工作,它就不必等到所有其他线程都完成相同的循环。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/6315923

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档