OPENMP Iteriation永不退出

时间:2011-12-02 21:05:22

标签: iteration openmp reduction

我正在尝试创建迭代进程的代码 ,同时将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;
}

2 个答案:

答案 0 :(得分:1)

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

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

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

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

答案 1 :(得分:0)

您的程序具有未指定的行为。请参见OpenMP 5.0 specification 1 中的第2.8节:

  

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

这意味着任何类型的分支(ifwhile等)其条件可能因#pragma omp for(或其他任何分支)上的不同线程而不同其他 worksharing构造)是非法的:

#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版本中的相应部分。