嵌套循环中未正确忽略内部循环的 Pragma omp parallel

Posted

技术标签:

【中文标题】嵌套循环中未正确忽略内部循环的 Pragma omp parallel【英文标题】:Pragma omp parallel for inside inner loop is not correctly ignored in nested loop 【发布时间】:2020-08-08 07:33:18 【问题描述】:

我正在尝试实现以下代码,以了解如何通过嵌套循环管理 OpenMP 线程,其中每个内部/外部循环在函数及其调用者中单独实现。

每个循环都是用语句实现的 #pragma omp parallel for,我假设内部循环的 pragma 被忽略。

为了看到这一点,我在每个循环中打印了线程号。

然后,我可以看到以下内容,其中内部循环中的线程 id 始终为零,与调用者对应的线程号不同。为什么会这样?

Calling 0 from 0
Calling 2 from 1
Calling 6 from 4
Calling 8 from 6
Calling 4 from 2
Calling 7 from 5
Calling 5 from 3
    Calling 0 from 0  // Expecting 3
    Calling 1 from 0
    Calling 2 from 0
    Calling 3 from 0
    Calling 0 from 0
    Calling 1 from 0
    Calling 2 from 0
    Calling 3 from 0
    Calling 0 from 0
    Calling 0 from 0
    Calling 0 from 0
    Calling 1 from 0
    Calling 2 from 0
    Calling 3 from 0
Calling 9 from 7
    Calling 1 from 0 // Expecting 7
    Calling 2 from 0
    Calling 3 from 0
    Calling 0 from 0
Calling 3 from 1
    Calling 0 from 0 // Expecting 1
    Calling 1 from 0
    Calling 2 from 0
    Calling 1 from 0
    Calling 2 from 0
    Calling 3 from 0
    Calling 3 from 0
    Calling 1 from 0
    Calling 2 from 0
    Calling 3 from 0
    Calling 0 from 0
    Calling 1 from 0
    Calling 2 from 0
    Calling 3 from 0
    Calling 0 from 0
    Calling 1 from 0
    Calling 2 from 0
    Calling 3 from 0
Calling 1 from 0
    Calling 0 from 0
    Calling 1 from 0
    Calling 2 from 0
    Calling 3 from 0
#include <vector>                                                                                                                                                                                                                                                          
#include <omp.h>
#include <iostream>
#include <cstdio>
#include <limits>
#include <cstdint>
#include <cinttypes>

using namespace std;

const size_t  kM = 4;

struct Mat

 int elem[kM];

 Mat(const Mat& copy)
 
  for (size_t i = 0; i<kM; ++i)
   this->elem[i] = copy.elem[i];
 
 Mat()
 
  for (size_t i = 0; i<kM; ++i)
    elem[i] = 0;
 

 void do_mat(Mat& m)
 
  #pragma omp parallel for
  for (int i = 0; i<kM; ++i)
  
    printf(" \tCalling %d from %d\n", i, omp_get_thread_num());
    elem[i] += m.elem[i];
  
 
;

int main ()

  const int kN = 10;
  vector<Mat> matrices(kN);

  Mat m;
  #pragma omp parallel for
  for (int i = 0; i < kN; i++)
  
    int tid = omp_get_thread_num();
    printf("Calling %d from %d\n", i, tid);
    matrices[i].do_mat(m);
  

  return 0;
          

【问题讨论】:

【参考方案1】:

我不确定我是否理解您的预期,但您得到的结果完全符合预期。

默认情况下,OpenMP 嵌套并行性被禁用,这意味着任何嵌套的 parallel 区域将创建与遇到它们的外部级别的线程一样多的 1 个线程组。

在您的情况下,您最外层的 parallel 区域创建了一个由 8 个线程组成的团队。这些中的每一个都将到达最里面的parallel 区域,并创建一个二级 1 线程团队。这些二级线程中的每一个,在其自己的团队中,排名为 0,因此您拥有打印的 0。

使用 g++ 9.3.0 编译的相同代码,通过设置 2 个环境变量 OMP_NUM_THREADSOMP_NESTED,我得到以下信息:

OMP_NUM_THREADS="2,3" OMP_NESTED=true ./a.out 
Calling 0 from 0
Calling 5 from 1
    Calling 0 from 0
    Calling 1 from 0
    Calling 2 from 1
    Calling 0 from 0
    Calling 1 from 0
    Calling 3 from 2
    Calling 3 from 2
    Calling 2 from 1
Calling 6 from 1
Calling 1 from 0
    Calling 0 from 0
    Calling 1 from 0
    Calling 3 from 2
    Calling 2 from 1
Calling 2 from 0
    Calling 0 from 0
    Calling 1 from 0
    Calling 2 from 1
    Calling 3 from 2
    Calling 0 from 0
    Calling 1 from 0
    Calling 3 from 2
    Calling 2 from 1
Calling 3 from 0
Calling 7 from 1
    Calling 0 from 0
    Calling 3 from 2
    Calling 2 from 1
    Calling 3 from 2
    Calling 0 from 0
    Calling 1 from 0
    Calling 1 from 0
    Calling 2 from 1
Calling 4 from 0
Calling 8 from 1
    Calling 0 from 0
    Calling 3 from 2
    Calling 2 from 1
    Calling 2 from 1
    Calling 0 from 0
    Calling 1 from 0
    Calling 3 from 2
    Calling 1 from 0
Calling 9 from 1
    Calling 2 from 1
    Calling 0 from 0
    Calling 1 from 0
    Calling 3 from 2

也许这更符合您的预期?

【讨论】:

内循环中出现的数字是什么意思?在我的例子中,一切都显示为 0。但是,我们有 8 个线程(编号 0 - 7)并行执行的外部循环。我怀疑内部循环是否由编号为 0 的同一线程处理。 可能你的误解来自omp_thread_num()返回的是最近遇到的队伍(最里面的)的线程id,不一定是第一个。因此,您看到的 0 与此完全一致,因为所有线程都是二级并行区域上的主线程,有 8 个不同的团队,每个团队有 1 个线程。在我的版本中,我们得到了 2 个二级团队,每个团队创建了 3 个线程。这就是为什么你会看到 ids 从 0 到 2 的原因。现在有意义吗? 啊,我明白了。感谢您的评论。当我删除了内部循环的 pragma 语句时,我的期望就来了。这是否意味着在内部/外部循环中使用omp parallel for 语句(没有任何特定选项)在内循环中会产生额外的开销?【参考方案2】:

除非您为 OpenMP 提供特殊选项,否则它会尝试在编译时拆分工作,并且很难使用嵌套并行性,因此它甚至不会尝试。

您可以参考this *** question 获取建议(例如,在 OpenMP 3.0+ 中使用collapse

【讨论】:

以上是关于嵌套循环中未正确忽略内部循环的 Pragma omp parallel的主要内容,如果未能解决你的问题,请参考以下文章

如何优化并行嵌套循环?

嵌套查询 - 内部查询循环结束外部查询循环

Linux Shell编程(17)——嵌套循环

在另一个并行循环中调用函数时,函数中的“pragma omp parallel for”无效

Mysql算法内部算法 - 嵌套循环连接算法

自学Linux Shell12.6-嵌套循环for命令