1

我正在尝试创建迭代过程的代码,同时将 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 terminated\n");
    return 0;
}
4

2 回答 2

2

这是一个有趣的小问题。我没有压倒性的 openmp 经验,但是在对您的代码进行一些实验之后,我认为问题是由于进入并行 for 循环时缺乏同步引起的(插入 write 语句以“监视”您的代码)。

您可以通过在并行 for 循环之前插入屏障来使代码正常工作:

#pragma omp barrier
#pragma omp for reduction(-:error)
   for(int i=0; i<10; ++i)

没有那个障碍并且在2个线程上运行,一个线程将第二次进入for循环并减少error到5,而另一个线程根本不会进入第二个for循环,使系统处于一种并行for循环的奇怪状态循环已被一个线程执行,但另一个线程拒绝加入。这肯定是关于在并行循环中写入共享变量并将它们用作其他地方的控制变量的警告。

于 2011-12-03T15:06:36.183 回答
0

您的程序具有未指定的行为请参阅OpenMP 5.0 规范1中的第 2.8 节:

团队中的所有线程都必须遇到每个工作共享区域,或者根本不遇到每个工作共享区域

这意味着任何类型的分支(ifwhile)对于围绕 a (或任何其他工作共享构造)的不同线程的条件可能不同是非法的:#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,这两个组永远不会再聚在一起......


之前在沃尔特的回答中建议的显式障碍#pragma omp for通过分离共享变量的读取和写入来解决这个问题error。进一步来说:

  • 每个线程都检查条件,并且对所有线程都是一样的——要么全部要么没有进入while循环体。
  • 如果他们进入while循环:
    • 他们都在明确的障碍处等待。
    • 他们都遇到了#pragma omp for
    • 在 for 循环中,它们更新error.
    • 他们都在隐蔽的屏障尽头等待#pragma omp for。(屏障做了一个隐含的flush,这意味着所有线程都看到 的最终值error。)
    • 返回开始。
  • while循环 后:
    • 他们都在隐蔽的屏障尽头等待#pragma omp parallel
    • 完毕。

当然,现在所有线程都执行for循环,这并没有“最小化并行化开销”,这是您想要的。我想你必须重新构建你的代码才能达到这个目标。也许使用#pragma omp task代替#pragma omp for可能是一个好方法,但这取决于您的实际数据结构和算法的细节。


注意:您可以通过向 中添加nowait子句来摆脱死锁#pragma omp for,但这将是一种 hack,并且您的程序仍然会有未指定的行为


1: ...或其他 OpenMP 版本中的相应部分。

于 2018-11-18T13:51:38.040 回答