OpenMP標準では、囲んでいる領域がアクティブである場合にのみtrueomp_in_parallel()
を返すと規定されています。アクティブ領域は、複数のスレッドで構成されるチームによって実行される領域として定義されます。あなたの場合、スレッドが1つしかないため、非アクティブな並列領域があります。したがって、が返され、が実行されます。OpenMP標準が例外を同じ並列領域とスレッドに制限しているため、エラーが発生します。parallel
parallel
omp_in_parallel()
false
throw
throw
リージョン内で実行されるとparallel
、同じリージョン内で実行が再開されparallel
、例外をスローした同じスレッドがそれをキャッチする必要があります。
これにより、例外が並列領域を未処理のまま通過するため、コードが不適合になります。同じエラーがIntelOpenMPランタイムによって発行されるため、GCC固有の動作ではありません。
実際に起こることは、GCCがtry/catch
キャッチオール例外フィルターを使用してコードをブロックにラップすることによって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
}
}
(インテル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
}
-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
アクティブかどうかに関係なく、コールを囲むリージョンのネストのレベルを返します。