インテル® C++ コンパイラー 18.0 デベロッパー・ガイドおよびリファレンス

例外処理

インテル® Cilk™ Plus は古い機能 (非推奨) です。代わりに、OpenMP* またはインテル® TBB を使用してください。詳細は、「インテル® Cilk™ Plus の代わりに OpenMP* またはインテル® TBB を使用するためのアプリケーションの移行」を参照してください。

インテル® Cilk™ Plus では、C++ 例外処理のセマンティクスを厳密に再現しようとします。つまり、一般に例外が解決されるまでの間は並列化が制限されることになるので、例外処理においてプログラムが並列性に依存しないようにする必要があります。次に、例外処理のロジックを示します。

例外をスローしても、例外がスローされたストランドの既存の子ストランドや同レベルのストランドはアボートしません。通常どおりに実行され、完了します。

try ブロックに cilk_spawncilk_sync が含まれている場合は、try ブロックの初めと終わりに(デストラクターが呼び出された後に) 暗黙的な cilk_sync があります。例外により try ブロック、関数ブロック、または cilk_for 本体を終了する前に同期が自動的に実行されます。(cilk_for 内の同期スコープは、同じループ内のスポーンに制限されます。) catch ブロックの実行を開始する時点で、関数にアクティブな子はありません。このような暗黙的な同期は、プログラムのシリアル実行で同じ catch 節を実行した場合と同じになるようにします。

暗黙的な同期は、プログラムの並列性の妨げとなることがあります。特に、次のような try ブロックの前の暗黙的な同期は、try ブロックの前にあるスポーンを時期尚早に同期してしまう可能性があります。

void func() {
   cilk_spawn f();
   try { // 暗黙の同期により f() が並列に実行されない
     cilk_spawn g();
     h();
   }
   catch (...){
     // g() または h() の例外は処理されるが、f() の例外は処理されない
   }
}

このことが問題になる場合は、暗黙的な同期を抑止する方法が 2 つあります。1 つ目は、try ブロックの本体を別の関数に移動します。cilk_spawn の記述を try ブロックの外側へ移動することで、try ブロックの始まりと終わりで cilk_sync が自動的に生成されないようにします。前述のコード例では、次のように変更します。

void gh()
{
   cilk_spawn g();
   h();
}

void func() {
   cilk_spawn f();
   try { // 本体に cilk_spawn が含まれないので暗黙の同期はない
     gh();
   }
   catch (...){
     // gh() の例外は処理されるが、f() の例外は処理されない
   }
}

2 つ目は、try ブロックの本体または try/catch 文全体を 1 回だけ実行される cilk_for ループにブロック化します。cilk_for ループの本体は、スポーンや同期を含むコンテキストからは分離されるため、スポーンや同期は周囲の関数に影響しません。この方法を多用する場合、マクロが役立ちます。前述のコード例では、次のように変更します。

#define CILK_TRY cilk_for (int _temp = 0; _temp < 1; ++_temp) try

void func() {
   cilk_spawn f();
   CILK_TRY { // try が cilk_for 内にあるため f() を同期しない
     cilk_spawn g();
     h();
   }
   catch (...){
     // g() または h() の例外は処理されるが、f() の例外は処理されない
   }
}

Windows* 構造化例外処理

Microsoft* Windows* の構造化例外処理 (SEH) を使用する場合には (特に /EHa コンパイラー・オプションと C/C++ の __try、__except、__finally__leave 拡張では)、いくつかの制限があります。スチールが行われた後、対応する cilk_sync の前に SEH 例外がスローされると、SEH は失敗します。この場合、ランタイムは状況を認識し、プログラムを終了して、適切なエラーコードを返します。