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

cilk_spawn

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

キーワードの記述方法は _Cilk_spawn です。ヘッダーファイル <cilk/cilk.h> には別の記述方法 (cilk_spawn) を利用するためのマクロが定義されています。ここでは、cilk.h で定義されている簡易版を使用します。

cilk_spawn キーワードは、関数呼び出し文を変更して、関数が呼び出し元と並列に実行すること (ただし、必ずしも並列に実行する必要はないこと) をランタイムシステムに指示します。cilk_spawn 文は、次のいずれかの形式で指定します。

type var =  cilk_spawn func(args); // func () returns a value

var = cilk_spawn func(args); // func () returns a value 

cilk_spawn func(args); // func () may return void

func は、現在のストランドと並列に実行される関数の名前です。ストランドは、並列動作を含まない一連のシリアル命令です。現在のルーチンの cilk_spwan の後のコードを、func で並列実行することができます。func(args) は、通常の (C スタイルの) 関数、メンバー関数、ラムダ関数、または operator() を多重定義する関数オブジェクトへの呼び出しです。

var は、func の戻り値から初期化または割り当てを行うことができる変数です。(戻り値と同じ型または互換性のある型です。) この変数は、関数呼び出しの結果を受け取るため、レシーバーと呼ばれます。レシーバーは void 関数には指定できません。

args は、スポーンされる関数に渡す一連の引数です。この引数は、実際にスポーンされる前に評価されます。参照渡しやアドレス渡しの引数は、少なくとも次の cilk_sync まで有効でなければなりません。そうしなければ、スポーンされた関数は、引数が破棄された後にそれを利用しようとするかもしれません。これはデータ競合の典型的な例です。

cilk_spawn は、代入の右辺に含まれる唯一の式でなければなりません。また、大きな式の一部であってはなりません。例えば、次の代入は許可されていません。コンパイラーによりエラーが出力されます。

var = var2 + cilk_spawn func(args);

スポーンされた関数はその関数をスポーンした関数の子と呼ばれ、cilk_spawn 文を実行する関数はスポーンされた関数の親と呼ばれます。

関数はどのような数式表現によってでもスポーンできます。例えば、次のように、関数ポインターやメンバー関数ポインターを使用することもできます。

var = cilk_spawn (object.*pointer) (args);

しかし、次のようにほかの関数をスポーンし、その戻り値を関数に渡すことはできません。

g(cilk_spawn f()); // Not allowed

f()g() の両方の呼び出しをスポーンする場合は、これは、C++11 のラムダを使用して簡単に記述することができます。

cilk_spawn [&]{ g(f()); }();

上記の表現は、次の方法とは異なることに注意してください。

cilk_spawn g(f());

後者では、f() は親関数で実行され、その戻り値を引数として g() がスポーンされます。前者の関数では、f()g() はともにスポーンされた子で実行されます。

名前付きラムダ関数がスポーンされた場合、ラムダ関数は少なくとも次の同期まで有効であることに注意してください。そうしなければ、ラムダ関数のデストラクターはスポーンされた呼び出しと競合します。次に例を示します。

double i = g();
if (some condition) {
   // i の値をキャプチャーする名前付きラムダ関数
   auto f = [=i]() { double d = sin(i); f(d); };
   cilk_spawn f();
} // f のデストラクターがスポーンを含む並列領域にある
call.