如何在OpenMP并行区域内找到是否

How to find out if inside an openMP parallel region?

本文关键字:是否 区域 并行 OpenMP      更新时间:2023-10-16

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

omp_in_parallel();

决定是抛出异常还是写出错误消息并终止。但是,在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
  }
}

不会导致错误:'3',但在扔出'int'aport'aport 的实例后终止了。

这是正确的行为(omp_in_parallel())吗?(OpenMP标准似乎很模糊)还是GCC中的错误?如何修复上述do_something()代码,以便仅在不在平行区域中抛出?

OpenMP标准指出,omp_in_parallel()返回 true 时,仅当包含parallel区域处于活动状态时。活动的parallel区域被定义为由由组成的团队执行的一个多个线程。在您的情况下,由于只有一个线程,因此您有一个不活动的平行区域。因此,omp_in_parallel()返回false,并且执行throw。之所以出错,是因为OpenMP标准将异常限制在同一并行区域和线程:

parallel区域内执行的throw必须导致执行以在同一parallel区域内恢复,并且抛出异常的同一线程必须捕获它。

这使您的代码不合格,因为您让异常通过并通过并行区域进行。Intel 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
  }

是终止消息的责任。

要解决这一问题,整个try/catch块应为内部 不反之亦然:

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

(添加了额外的块以使Intel C 编译器感到高兴;对于GCC来说,这不是必需的)

这将按预期输出error: '3'

编辑:有趣的是,您的异常处理程序甚至没有将其纳入最后的二进制文件。即使考虑到GCC的默认优化级别(即使用g++ -fopenmp -o prog prog.cc编译),冗余消除器也能够检测到您的异常处理程序将由于隐含的内部异常处理程序而无法达到您的异常处理程序,因此您的处理程序被删除。然后,编译器发现隐式终止处理程序也是冗余的,因为整个过程中已经有一个顶级异常处理程序,可以执行相同的操作(CALL 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
}

一个人可以爱上-fdump-tree-all GCC选项。

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

void do_something()
{
   if(omp_get_level() == 0)
     throw 3;
}

omp_get_level()返回包含呼叫的parallel区域的嵌套级别,无论它们是否处于活动状态。