インテル® C++ コンパイラー XE 13.1 ユーザー・リファレンス・ガイド
インテル® Cilk™ Plus では、C++ 例外処理のセマンティクスを厳密に再現しようとします。 つまり、一般に例外が解決されるまでの間は並列化が制限されることになるので、例外処理においてプログラムが並列性に依存しないようにする必要があります。 次に、例外処理のロジックを示します。
スポーンされた子で例外がスローされ、それがキャッチされなかった場合、次の同期ポイントで親が再び例外をスローします。
親および別の子が例外をスローした場合、シリアル実行で最初にスローされた例外が優先されます。 論理的に後にスローされた例外は破棄されます。現在、同時にスローされた複数の例外をキャッチするメカニズムはありません。
例外をスローしても、例外がスローされたストランドの既存の子ストランドや同レベルのストランドはアボートしません。通常どおりに実行され、完了します。
try ブロック に cilk_spawn や cilk_sync が含まれている場合は、try ブロックの初めと終わりに (デストラクターが呼び出された後に) 暗黙的な cilk_sync があります。 例外により try ブロック、関数ブロック、または cilk_for 本体を終了する前に同期が自動的に実行されます。 (cilk_for 内の同期スコープは、同じループ内のスポーンに制限されます。) catch ブロックの実行を開始する時点で、関数にアクティブな子はありません。 このような暗黙的な同期は、プログラムのシリアル実行で同じ catch 節を実行した場合と同じになるようにします。
暗黙的な同期は、プログラムの並列性の妨げとなることがあります。特に、次のような try ブロックの前の暗黙的な同期は、try ブロックの前にあるスポーンを時期尚早に同期してしまう可能性があります。
void func() { cilk_spawn f(); try { // oops! implicit sync prevents parallel execution of f() cilk_spawn g(); h(); } catch (...) { // Handle exceptions from g() or h(), but not f() } }
このことが問題になる場合は、暗黙的な同期を抑止する方法がいくつかあります。
try ブロックの本体を別の関数に移動します。 cilk_spawn の記述を try ブロックの外側へ移動することで、try ブロックの始まりと終わりで cilk_sync が自動的に生成されないようにします。 前述のコード例では、次のように変更します。
void gh() { cilk_spawn g(); h(); } void func() { cilk_spawn f(); try { // no cilk_spawn in body, so no implicit sync gh(); } catch (...) { // Handle exceptions from gh(), but not f() } }
try/catch 文全体または 1 回だけ実行される cilk_for ループに入る try ブロックの本体だけをブロック化します。 cilk_for ループの本体は、スポーンや同期を含むコンテキストからは分離されるため、スポーンや同期は周囲の関数に影響しません。 この方法を多用する場合、マクロが役立ちます。前述のコード例では、次のように変更します。
#define CILK_TRY cilk_for (int _temp = 0; _temp < 1; ++_temp) try void func() { cilk_spawn f(); CILK_TRY { // try is within cilk_for, so does not sync f() cilk_spawn g(); h(); } catch (...) { // Handle exceptions from g() or h(), but not f() } }
Windows* 構造化例外処理
Microsoft* Windows* の構造化例外処理 (SEH) を使用する場合には (特に /EHa コンパイラー・オプションと C/C++ の __try、__except、__finally、__leave 拡張では)、いくつかの制限があります。 スチールが行われた後、対応する cilk_sync の前に SEH 例外がスローされると、SEH は失敗します。 この場合、ランタイムは状況を認識し、プログラムを終了して、適切なエラーコードを返します。