OpenMP 嵌套 for 循环给出意外结果

OpenMP nested for loop gives unexpected result

我正在学习 OpenMP,下面带有嵌套 for 循环的代码在线程数 > 1 的情况下给出了意外结果。我希望这里只有外部循环会被并行化,我希望有 4 行输出。我正在使用 gcc 4.8.4.

#pragma omp parallel
{
      const int nthreads = omp_get_num_threads();
      const int ithread = omp_get_thread_num();

      #pragma omp for
      for (out = 0; out < 2; out++) {
        for (in = 0; in < 2; in++) {
          printf("id= %d of %d, out= %d, in= %d\n", ithread, nthreads, out, in);
        }
      }
}

如果我设置 OMP_NUM_THREADS=1,我会得到 4 行的预期输出:

id= 0 of 1, out= 0, in= 0
id= 0 of 1, out= 0, in= 1
id= 0 of 1, out= 1, in= 0
id= 0 of 1, out= 1, in= 1

但是,OMP_NUM_THREADS=2 漏掉了一行输出!

id= 0 of 2, out= 0, in= 0
id= 1 of 2, out= 1, in= 0
id= 0 of 2, out= 0, in= 1

将内部循环设置为 for (in = 0; in < 3; in++) 只给出 4 行输出而不是预期的 6 行!

id= 0 of 2, out= 0, in= 0
id= 1 of 2, out= 1, in= 0
id= 0 of 2, out= 0, in= 1
id= 1 of 2, out= 1, in= 2

我是不是做错了什么?请帮我解决这个问题。谢谢。

看起来您的内部循环变量似乎是在并行块开始之前声明的。这意味着它在所有线程之间隐式共享。也许您正在尝试并行化 2D 循环,但您尝试的方法无法实现;相反,每个内部循环将以不确定的方式与其他 运行 内部循环冲突。

如果您想并行化一个 2D 循环,您必须对所有元素使用一个循环,然后手动计算索引,例如for(int i=0;i<2*2;i++){ int out=i/2; int in=i%2; ... }。现在您可以毫无问题地使 i 并行循环。正如 High Performance Mark 指出的那样, 有一个专门的构造:OpenMP and nested loops?

如果您可以只并行化一个维度(这通常就足够了),您要么需要在并行循环中声明内部变量:

int out;
#pragma omp parallel for
for(out=0;out<2;out++){
    int in;
    for(in=0;in<2;in++){
        // action
    }
}

或者显式地将内部变量标记为私有:

int in, out;
#pragma omp parallel for private(in)
for(out=0;out<2;out++){
    for(in=0;in<2;in++){
        // action
    }
}

这些都使外循环并行,内循环对每个线程都是私有的,防止一个线程的当前状态改变另一个线程的状态。如果您使用的是 c99 之前的 c 语言,则第二种风格很有用,但除此之外,我建议只将声明移动到循环内更利于显示意图。

这个问题包含一些关于设置私有变量的有用注释:OpenMP: are local variables automatically private?


最后,你应该避免直接从多个线程打印到同一个输出;这些函数的线程安全是模棱两可的(尽管特别是在 Linux 上似乎定义得更好)。最好把打印抽象成一个线程安全的缓冲区,用单线程写缓冲区。

我看到两个问题:

  • 我看不到第一个 omp parallel 语句的用途,应该将其删除
  • in 应该声明为私有的(这样每个线程都有自己的副本)

    #pragma omp parallel for private(in) /* each thread needs its own copy of in */
      for (out = 0; out < 2; out++) {
        for (in = 0; in < 2; in++) {
          printf("id= %d of %d, out= %d, in= %d\n", ithread, nthreads, out, in);
        }
      }
    

    }