我正在尝试创建一个迭代过程的代码,同时保持while在并行区域内,以尽量减少并行化开销
代码是这样的问题是它永远不会退出,所以如果可能的话,我想知道你对这个
的想法#include <stdio.h>
#include <omp.h>
int main(int argc, char **argv)
{
float error = 20;
#pragma omp parallel shared(error)
{
while (error > 5)
{
#pragma omp for reduction(-:error)
for (int i=0; i<10; ++i)
{
error -= 1;
}
}
}
fprintf(stderr, "Program terminatedn");
return 0;
}
这是一个有趣的小问题。我没有压倒性的openmp经验,但在对你的代码进行了一些实验之后,我认为问题是由于进入并行for循环(插入write语句以"观察"你的代码)时缺乏同步引起的。
您可以通过在并行for循环之前插入一个屏障来使代码工作:
#pragma omp barrier
#pragma omp for reduction(-:error)
for(int i=0; i<10; ++i)
如果没有这个屏障,并且运行在两个线程上,一个线程将第二次进入for循环,并将error
降低到5,这时另一个线程根本不会进入第二个for循环,使系统处于一个线程执行并行for循环,但另一个线程拒绝加入的奇怪状态。在并行循环和中写入共享变量,将它们用作其他地方的控制变量,这无疑是一个警告。
您的程序有未指定行为。参见OpenMP 5.0规范中的2.8节1:
每个工作共享区域必须由团队中的所有线程遇到,或者根本没有线程遇到
这意味着任何类型的分支(if
, while
等)在#pragma omp for
(或任何其他工作共享构造)周围的不同线程的条件可能不同:
#pragma omp parallel
{
if (...true for some threads, false for others...) // ILLEGAL!
{
#pragma omp for
for (...) ...
}
while (...true for some threads, false for others...) // ILLEGAL!
{
#pragma omp for
for (...) ...
}
}
在您的示例中,此未指定的行为可能导致以下事件序列:
- 每个线程检查条件,但可能不是所有线程都一样-有些进入
while
循环,有些没有。 - 如果进入
while
循环:- 他们遇到了
#pragma omp for
. 在for循环中,他们更新
error
。 - 他们遇到了
- 它们在
#pragma omp for
的隐式屏障处等待。
while
循环:- 它们在
#pragma omp parallel
的隐式屏障处等待。
当一个OpenMP线程到达一个屏障时,它等待,直到它的团队中的所有线程都到达屏障。 #pragma omp for
的隐式屏障不适应遇到该构造的线程数量。在您的例子中,一些线程永远不会到达for
循环结束时的屏障(因为对它们来说while
条件为假)。它们已经跳过了while
循环,现在在#pragma omp parallel
末尾的隐式屏障处等待。
结果是一个死锁:一些线程在#pragma omp for
的末尾等待,另一些在#pragma omp parallel
的末尾等待,这两个组永远不会再聚集在一起…
在Walter的回答中建议的#pragma omp for
之前的显式屏障通过分离共享变量error
的读和写来解决这个问题。更具体地说:
- 每个线程检查条件,并且对所有线程都是一样的-进入
while
循环体的是all或none。 - 如果进入
while
循环:- 它们都在显式屏障处等待。
- 他们都遇到了
#pragma omp for
. 在for循环中,他们更新
error
. - 它们都在
#pragma omp for
末尾的隐式屏障处等待。(屏障执行隐式flush
,这意味着所有线程都看到error
的最终值。) - 返回开始
while
循环后:- 它们都在
#pragma omp parallel
末尾的隐式屏障处等待。 - 。
当然,现在所有线程都执行for
循环,这并没有"最小化并行化开销",这是你想要的。我猜你将不得不重新构造你的代码来达到这个目标。也许使用#pragma omp task
代替#pragma omp for
可能是一个好方法,但这取决于实际数据结构和算法的细节。
注意:您可以通过向
#pragma omp for
添加nowait
子句来摆脱死锁,但这将是一个hack,并且您的程序仍然会有未指定的行为
。<子> 1:……或其他OpenMP版本中的相应部分。 子>