如何找出openMP并行区域内?

Wal*_*ter 5 c c++ openmp

在我的代码中,我想避免在任何openMP并行区域内抛出异常(因为如果未在同一区域内捕获,则会导致未处理的异常).为此,我尝试使用openmp运行时库函数

omp_in_parallel();
Run Code Online (Sandbox Code Playgroud)

决定是抛出异常还是写出错误消息并终止.但是,在gcc 4.7.0下,如果只有一个并行区域的线程,这将不起作用:

#include <iostream>
#include <omp.h>

void do_something()
{
  if(!omp_in_parallel())           // omp_in_parallel() returns false!
    throw 3;                       // so should be able to safely throw
}

int main()
{
  omp_set_num_threads(1);
  try {
#   pragma omp parallel
    do_something();
  } catch(int e) {
    std::cerr<<"error: '"<<e<<"'\n";  // never gets here
  }
}
Run Code Online (Sandbox Code Playgroud)

不会导致错误:'3'但是在抛出'int'Abort实例后调用终止.

这是正确的行为omp_in_parallel()吗?(openMP标准显得足够模糊)或gcc中的错误?如何修复上面的代码,do_something()以便仅在不在并行区域时抛出?

Hri*_*iev 9

当且仅当封闭区域处于活动状态时,OpenMP标准声明omp_in_parallel()返回trueparallel.活动parallel区域被定义为由包含多个线程的团队执行的活动区域.在您的情况下,您有一个非活动的并行区域,因为只有一个线程.因此omp_in_parallel()返回falsethrow执行.它错误,因为OpenMP标准限制同一并行区域和线程的异常:

一个throw一个内部执行parallel区必须使执行相同的内恢复parallel区,并抛出异常同一线程必须抓住它.

这使得您的代码不符合要求,因为您通过并行区域处理未处理的异常.英特尔OpenMP运行时发出相同的错误,因此它不是特定于GCC的行为.

实际上会发生的事情是GCC通过将代码包装在try/catch具有catch-all异常过滤器的块中来转换OpenMP区域:

#pragma omp parallel [child fn: main.omp_fn.0 (???)]
  {
    try
      {
        do_something ();
      }
    catch
      {
        <<<eh_filter (NULL)>>>
          {
            terminate ();
          }
      }
    #pragma omp return
  }
Run Code Online (Sandbox Code Playgroud)

正是这个全部捕获的异常过滤器负责终止消息.

要解决这个问题,整个try/catch块应该并行区域,而不是相反:

# pragma omp parallel
{
   try {
      do_something();
   } catch(int e) {
      std::cerr<<"error: '"<<e<<"'\n";  // never gets here
   }
}
Run Code Online (Sandbox Code Playgroud)

(添加了附加块以使英特尔C++编译器满意;对于GCC来说并不是绝对必要的)

error: '3'按预期输出.

编辑:有趣的是,你的异常处理程序甚至没有进入最终的二进制文件.即使给定GCC的默认优化级别(即编译g++ -fopenmp -o prog prog.cc),冗余消除器也能够检测到由于隐式内部异常处理程序而永远不会到达异常处理程序,因此删除了处理程序.然后编译器发现隐式终止处理程序也是冗余的,因为已经有一个顶级异常处理程序用于执行相同(调用terminate())的整个进程,因此甚至删除了隐式处理程序.最终的代码是如此精简和卑鄙 - 根本没有异常处理程序:

;; Function int main() (main, funcdef_no=970, decl_uid=20816, cgraph_uid=212)

int main() ()
{
  int e;
  int D.20855;
  struct basic_ostream & D.20854;
  struct basic_ostream & D.20853;
  void * D.20852;
  register int * D.20819;

<bb 2>:
  omp_set_num_threads (1);
  __builtin_GOMP_parallel_start (main._omp_fn.0, 0B, 0);
  main._omp_fn.0 (0B);
  __builtin_GOMP_parallel_end ();
  D.20855_1 = 0;
  // <------ See, ma', no exception handling at all :)

<L0>:
  return D.20855_1;

}

;; Function <built-in> (main._omp_fn.0, funcdef_no=976, decl_uid=20857, cgraph_uid=222)

<built-in> (void * .omp_data_i)
{
<bb 2>:
  do_something ();
  return;
  // <------ See, ma', they've nuked the implicit termination handler

}
Run Code Online (Sandbox Code Playgroud)

人们喜欢-fdump-tree-allGCC选项.

编辑:关于如何修复的问题do_something()- 使用omp_get_level()而不是omp_in_parallel():

void do_something()
{
   if(omp_get_level() == 0)
     throw 3;
}
Run Code Online (Sandbox Code Playgroud)

omp_get_level()返回parallel包围调用的区域的嵌套级别,无论它们是否处于活动状态.