【问题标题】:OPENMP Iteriation never exitsOPENMP 迭代从不退出
【发布时间】:2012-01-11 20:32:33
【问题描述】:

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

【问题讨论】:

标签: iteration openmp reduction


【解决方案1】:

这是一个有趣的小问题。我没有压倒性的 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 循环,但另一个线程拒绝加入的奇怪状态。这肯定是对写入并行循环中的共享变量并在其他地方使用它们作为控制变量的警告。

【讨论】:

    【解决方案2】:

    您的程序有未指定的行为。请参阅OpenMP 5.0 specification1 中的第 2.8 节:

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

    这意味着任何类型的分支(ifwhile 等)其条件可能因不同线程而不同围绕#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 循环的主体。
    • 如果他们进入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,您的程序仍然会有未指定的行为 em>.


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

    【讨论】:

      猜你喜欢
      • 2016-04-13
      • 1970-01-01
      • 1970-01-01
      • 2021-12-15
      • 2017-01-19
      • 1970-01-01
      • 1970-01-01
      • 2019-02-08
      • 1970-01-01
      相关资源
      最近更新 更多