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

问题描述

|| 我正在研究OpenMP,遇到了以下示例:
#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和reduce子句。它是否正确?归约条款是否需要同步化?     

解决方法

        第二个循环和最后一个循环中的1有点多余。 OpenMP规范在区域结尾之前提到了“ 1”,因此也许可以保留。 但是第二个循环之前的ѭ1和它之后的显式壁垒相互抵消。 最后,关于
shared
private
子句。在您的代码中,“ 4”无效,而根本不应该使用“ 5”:如果需要线程专用变量,只需在并行区域内声明即可。特别是,您应该在循环内部而不是之前声明循环变量。 要使“ 4”有用,您需要告诉OpenMP默认情况下不应共享任何内容。您应该这样做以避免由于意外共享变量而导致的错误。通过指定
default(none)
来完成。这给我们留下了:
#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
    ,从某些方面看,这似乎是一个家庭作业问题,我讨厌为人们做。另一方面,上述答案并不完全正确,我认为应该予以纠正。 首先,虽然在此示例中,不需要共享和私有子句,但我不同意Konrad的观点,不应使用它们。人们并行化代码最常见的问题之一是,他们没有花时间去理解变量的使用方式。没有私有化和/或保护应该是的共享变量,导致了我看到的最大数量的问题。进行检查如何使用变量并将其放入适当的shared,private等子句中的练习将极大地减少您遇到的问题。 至于障碍的问题,第一个循环可以有一个nowait子句,因为在第二个循环中没有使用(a)计算的值。仅当在计算值之前不使用计算的值(c)(即没有依赖性)时,第二个循环才可以具有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,并且在第二个和第三个循环之间不需要任何屏障(隐式或显式)完全没有 现在我们进入第三个循环,以及您有关无等待和减少的问题。正如Michy指出的,OpenMP规范允许同时指定(减少和不等待)。但是,并非完全不需要同步即可完成还原。在该示例中,可以使用nowait删除隐式屏障(在第三循环的末尾)。这是因为在遇到并行区域的隐式屏障之前未使用缩减(总和)。 如果查看OpenMP V3.0规范的2.9.3.6节reduce子句,则会发现以下内容:   如果不使用nowait,则缩减计算将在结束时完成。   构造;但是,如果将减少子句用于不等待的构造   同样适用于原始列表项的访问将创建种族,因此具有   未指定效果,除非同步确保它们在所有线程都具有之后发生   执行所有的迭代或节构造,以及约简计算   已经完成并存储了该列表项的计算值。这很简单   通过障碍同步确保。 这意味着,如果您想在第三个循环之后在并行区域中使用sum变量,那么在使用它之前,您将需要一个屏障(隐式或显式)。就目前的例子而言,这是正确的。     ,OpenMP的特点是:   循环构造的语法如下:
#pragma omp for [clause[[,] clause] ... ] new-line
    for-loops
     where子句是以下之一:
 ...
 reduction(operator: list)
 ...
 nowait
因此,可以有更多的子句,因此可以同时有reduce和nowait语句。
reduction
子句中不需要显式同步-由于
reduction(+: sum)
和先前的屏障力
a
b
reduction
循环时具有最终值,因此对
sum
变量的添加是同步的。 “ 1”表示如果线程在循环中完成工作,则不必等待所有其他线程都将完成同一循环。