【问题标题】:OpenMP nesting not turning offOpenMP 嵌套未关闭
【发布时间】:2020-01-08 00:58:57
【问题描述】:

我正在尝试使用 OpenMP(4.5,通过 GCC 7.2.0)管理嵌套的并行区域,但在关闭嵌套时遇到了一些问题。

示例程序:

#include <stdio.h>
#include <omp.h>

void foobar() {
  int tid = omp_get_thread_num();
  #pragma omp parallel for
  for (int i = 0; i < 4; i++) {
    int otid = omp_get_thread_num();
    printf("%d | %d\n", tid, otid);
  }
}

int main(void) {
  omp_set_nested(0);
  #pragma omp parallel
  {
    foobar();
  }
  printf("\n");
  foobar();
  return 0;
}

我期望在这里发生的是对 foobar() 的并行区域和非并行调用都会吐出 4 行,这与

// parallel region foobar()
0 | 0
1 | 1
2 | 2
3 | 3
// serial region foobar()
0 | 0
0 | 1
0 | 2
0 | 3

因为我不允许嵌套并行。但是,我在具有正确 TID 的并行区域内得到了 16 行,但 OTID 始终为 0(即每个线程都生成 4 个自己的线程,并在其上执行整个循环),我在外部得到 4 行(即并行正如我所料,for 正在产生 4 个线程)

我觉得我在这里遗漏了一些非常明显的东西,有人可以为我解释一下吗?禁用嵌套是否应该将 omp parallel for 转换为常规 omp for,并相应地分配工作?

【问题讨论】:

  • 而不是描述你得到的输出,edit这个问题要包括实际输出。

标签: c++ c openmp


【解决方案1】:

您的问题来自错误的假设,即 omp for 指令将被解释并且相应的工作分布在线程之间,而不管 parallel 区域是否处于活动状态。不幸的是,在您的代码中,omp for 仅与函数foobar() 中声明的parallel 区域相关联。因此,当这个区域被激活时(意味着因为你禁用了嵌套并行,当foobar() 没有从另一个parallel 区域调用时)你的循环将分布在新生成的线程中。但如果不是,因为foobar() 是从另一个parallel 区域调用的,那么omp for 将被忽略,并且循环不会分布在调用线程中。因此,它们中的每一个都执行整个循环,从而导致您看到的 printf() 的复制。

一个可能的解决方案是这样的:

#include <stdio.h>
#include <omp.h>

void bar(int tid) {
  #pragma omp for
  for (int i = 0; i < 4; i++) {
    int otid = omp_get_thread_num();
    printf("%d | %d\n", tid, otid);
  }
}

void foobar() {
  int tid = omp_get_thread_num();
  int in_parallel = omp_in_parallel();
  if (!in_parallel) {
    #pragma omp parallel
    bar(tid);
  }
  else {
    bar(tid);
  }
}

int main() {
  #pragma omp parallel
  foobar();
  printf("\n");
  foobar();
  return 0;
}

我真的不觉得这个解决方案完全令人满意,但我现在没有看到更好的解决方案。也许以后我会得到一些启示......

编辑:好吧,我有另一个想法:反其道而行之,强制嵌套并行,每当从实际的parallel 区域调用函数时,只有一个活动线程:

#include <stdio.h>
#include <omp.h>

void foobar() {
  int tid = omp_get_thread_num();
  omp_set_nested(1);
  #pragma omp single
  #pragma omp parallel for
  for (int i = 0; i < 4; i++) {
    int otid = omp_get_thread_num();
    printf("%d | %d\n", tid, otid);
  }
}

int main() {
  #pragma omp parallel
  foobar();
  printf("\n");
  foobar();
  return 0;
}

这一次代码看起来更好,没有任何重复,并给出(例如):

$ OMP_NUM_THREADS=4 ./nested
3 | 2
3 | 3
3 | 1
3 | 0

0 | 3
0 | 1
0 | 0
0 | 2

【讨论】:

  • 不幸的是,这些解决方案在我的实际用例中不起作用。然而,这确实澄清了正在发生的事情。我想我误解了并行区域的动态范围是如何表现的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多