2011-06-11 54 views
9

NOWAIT和减少条款我学习OpenMP的,和整个下面的例子来: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循环中,有一个非等待和减少条款。它是否正确?减少条款是否需要同步?

+1

我很好奇,你在哪里找到这个例子? – 2011-06-11 12:31:30

+1

这个例子是我的教授给出的。这是为了解释明确的障碍的使用,但我不确定最终的回路是否正确。 – aperez 2011-06-11 12:35:33

回答

16

第二个和最后一个循环中的nowait有点多余。 OpenMP规范在该区域结束之前提到了nowait,因此这可能会留下。

但是第二个循环之前的nowait以及它之后的显式屏障是多余的。

而且特别是对于reduction没有必要。

此外,sharedprivate子句在C++中不是必需的:shared是多余的,并且不应使用private。如果你需要一个线程私有变量,只需在内声明这个并行区域。特别是在C++中你应该应该声明循环变量里面的循环,而不是之前。

​​
+0

Hey @Konrad Rudolph你能看看我的代码吗我不明白你为什么说我们不需要使用共享和私有的,我;我正在VS2012工作 – Gilad 2014-02-26 22:10:16

2

OpenMP speficication说:

循环结构的语法如下:

#pragma omp for [clause[[,] clause] ... ] new-line 
    for-loops 

where子句是下列之一:

... 
reduction(operator: list) 
... 
nowait 

所以有可以是更多的子句,因此可以有减少和现在的声明。

还有就是reduction子句中不需要显式同步的 - 加法到sum变量由于reduction(+: sum)和先前阻挡力ab同步在reduction回路的时间具有最终值。 nowait意味着如果线程完成循环中的工作,则不必等到所有其他线程完成相同的循环。

+0

感谢您的澄清。 – aperez 2011-06-11 13:30:57

8

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

首先,虽然在这个例子中共享和私人条款都不需要,我不同意康拉德他们不应该使用。人们并行化代码最常见的问题之一是,他们没有花时间来理解变量是如何被使用的。不是私有化和/或保护应该是的共享变量,这是我看到的最多问题。仔细研究如何使用变量并将它们放入合适的共享,私有等子句中,将大大减少您遇到的问题的数量。

至于有关障碍的问题,第一个循环可以有一个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节)。

现在在你的例子中,任何循环都没有显示任何时间表,所以这可能会也可能不会成立。原因是,默认时间表是实现定义的,而大多数实现当前将默认时间表定义为静态,但不能保证这一点。如果你的教授在所有三个循环中都有一个没有chunk-size的静态调度类型,那么nowait可以用在第一个和第二个循环上,而第二个和第二个循环之间不需要任何屏障(隐式或显式)第三个循环。

现在我们来到第三个循环以及关于nowait和reduce的问题。正如Michy指出的那样,OpenMP规范允许指定(还原和nowait)。但是,减少完成并不需要同步。在示例中,可以使用nowait删除隐式屏障(在第三个循环结束时)。这是因为在并行区域的隐式屏障遇到之前,还原(和)未被使用。

如果你看的OpenMP V3.0规范,第2.9.3.6条款减少,你会发现以下内容:

如果不使用NOWAIT,减少计算将在年底前完成 构造;然而,如果在还应用了nowait的构造上使用了reduction clause,那么对原始列表项的访问将创建一个竞赛,并因此具有 未指定的效果,除非同步确保在所有线程都执行完所有线程后执行 他们的迭代或部分结构,并且减少计算 已经完成并存储了该列表项目的计算值。这可以通过障碍同步确保最简单的 。

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

+0

谢谢你的非常详细的回答ejd!我同意你的看法,事实上,这看起来像是一个家庭作业问题,因为这不是我的代码 - 我刚刚通过学习示例开始学习OpenMP。如果你知道一个更合适的地方来问这些问题,或者一个标签来代表他们(类似于作业标签),请告诉我。 – aperez 2011-06-12 21:26:16

+0

我不同意使用'shared' /'private'子句,并且在我的回答中扩展了这个:如果使用私有变量,**使它们变为私有**。不要依赖预处理子句,在本地范围内声明它们。有*没有理由不在C++中这样做,实际上声明变量接近使用应该*总是*在C++中完成;由于这个原因,使用'private'是C++代码异味的明显标志。 – 2011-06-13 19:37:08

+3

Konrad - 由于该示例没有说C或C++,而在C89中,您不能执行所显示的内容,所以我认为这是有效的。最重要的是,正如我所说的,在过去的12年中,我看到的OpenMP中最常见的问题是用户没有花时间浏览并查看变量在代码中的使用方式。我个人认为,默认应该是要求用户说明如何使用每个变量。这肯定会减少发生的问题的数量。同时,我将回到工作中去帮助人们弄清楚他们做错了什么。 – ejd 2011-06-15 14:57:19