[英]How to find out if inside an openMP parallel region?
在我的代碼中,我想避免在任何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'Abort實例后調用終止 。
這是正確的行為( omp_in_parallel()
)嗎? (openMP標准顯得足夠模糊)或gcc中的錯誤? 如何修復do_something()
的上述代碼,以便僅在不在並行區域時拋出?
OpenMP標准規定,當且僅當封閉的parallel
區域處於活動狀態時, omp_in_parallel()
返回true 。 活動parallel
區域被定義為由包含多個線程的團隊執行的區域。 在您的情況下,您有一個非活動的並行區域,因為只有一個線程。 因此, omp_in_parallel()
返回false
並執行throw
。 它錯誤,因為OpenMP標准限制同一並行區域和線程的異常:
在
parallel
區域內執行的throw
必須導致執行在同一parallel
區域內恢復,並且拋出異常的同一線程必須捕獲它。
這使得您的代碼不符合要求,因為您通過並行區域處理未處理的異常。 英特爾OpenMP運行時發出相同的錯誤,因此它不是特定於GCC的行為。
實際上會發生的事情是GCC通過將代碼包裝在帶有catch-all異常過濾器的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
區域的嵌套級別,無論它們是否處於活動狀態。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.