HPC/並列プログラミングポータルでは、HPC(High Performance Computing)プログラミングや並列プログラミングに関する情報を集積・発信しています。 |
[記事一覧を見る]
Cilk Plusによる並列プログラミングでもっとも基本となるのが、「_Cilk_spawn」や「_Cilk_for」、「_Cilk_sync」キーワードを使った並列化だ。これらのキーワードで指定した処理やループの並列実行、そして処理の完了待ちを行える(表2)。なお、ソースコード中で「cilk/cilk.h」ではこれらはそれぞれ「cilk_spawn」や「cilk_sync」、「cilk_for」としてdefineされており、こちらを利用することが推奨されている。ドキュメントなどでは両方が混在している場合があるので注意して欲しい。
キーワード | 別名 | 説明 |
---|---|---|
_Cilk_spawn | cilk_spawn | 指定した処理を並列実行する |
_Cilk_sync | cilk_sync | _Cilk_spawnで生成した処理がすべて完了するまで待機する |
_Cilk_for | cilk_for | 並列実行させたいループを指定する(forループの代わりに使用する) |
並列実行したい処理に対し次のようにcilk_spawnキーワードを付けることで、その処理を並列実行できる。
cilk_spawn func1(a, b, c); ← 関数「func1(a, b, c)」を並列実行する d = cilk_spawn func2(a, b, c); ← 関数「func2(a, b, c)」を並列実行し、結果を変数dに格納する
cilk_spawnキーワードで処理の並列実行を開始した後、その処理の終了を待機するにはcilk_syncキーワードを使用する。
cilk_sync;
たとえば、ある関数「funcA()」と「funcB()」を並列実行させたい場合、コードは次のリスト3のようになる。
: : cilk_spawn funcA(); ← funcAを並列実行させる funcB(); ← funcAの終了を待たず、funcBが実行される cilk_sync; ← funcAとfuncBの終了を待つ
このとき、次のように関数の戻り値を直接他の関数の引数として与えることはできない。
g(cilk_spawn f()); ← このような記述はエラーとなる
このような場合、C++の拡張機能であるラムダ関数を使って次のように記述すれば良い。
cilk_spawn []{ g(f()); }();
ラムダ関数は現在策定中のC++新規格「C++0x」で標準化が予定されているC++の言語拡張であるが、Parallel Composerに含まれるインテル コンパイラーではすでに実装されており利用が可能だ。C++0xについては「インテル コンパイラーで試す次世代C++規格「C++0x」という記事で紹介しているので、興味のある方はこちらを参照していただきたい。
なお、これらの表記を次のように書いた場合エラーにはならないものの、「f()を実行した後、その戻り値を引数にg()を並列実行する」という動作となってしまう。
cilk_spawn g(f());
並列プログラミングでよく使われるテクニックとして、forループ内の処理を並列実行する、というものがある。cilk_forキーワードは、このような処理を簡潔に記述するためのキーワードだ。
たとえば、次のリスト4のようなコードを並列実行する例を考えよう。
int i, end; : : for (i = 0; i end; i++) { func(i); }
この場合、リスト5のようにコード中の「for」を「cilk_for」に書き換えるだけで、forループ内が並列実行されるようになる。
int i, end; : : cilk_for (i = 0; i end; i++) { ← ループ内が並列実行される func(i); }
ただし、cilk_forの利用にはいくつかの制限があり、すべてのforループを無条件に置き換えられるわけではない。
なお、cilk_forの引数にSTLのイテレータを使用することも可能だ。たとえば、次のようなforループはそのままcilk_forループに書き換えることができる。
for (T::iterator i(vec.begin()); i != vec.end(); ++i) { func(i); }
一方、次のようなforループはcilk_forに置き換えられない。
// cilk_forに置き換えられない例1:複数の変数を初期化している for (i = 0, j = 0; i end; i++) { func(i); } // cilk_forに置き換えられない例2:条件式内で関数を呼んでいる for (i = 0; compare_func(i); i++) { func(i); } // cilk_forに置き換えられない例3:制御変数で加算される値が一定でない for (i = 0; i end; i += calc(i)) { func(i); }
そのほか、cilk_forループ内ではcilk_for/cilk_syncの利用や__try、__except、__finally、__leaveによる例外処理も禁止されており、これらを利用しているforループもcilk_forに置き換えられない。
Cilk Plusを用いた並列処理の流れは、有向無閉路グラフ(Directed Acyclic Graph、DAG)というグラフで図示できる。たとえば次のリスト6のようなプログラムの場合、処理の流れは図4のように図示できる。
: : funcA(); cilk_spawn funcB(); ←① funcC(); cilk_spawn funcD(); ←② funcE(); cilk_sync; ←③ funcF(); : :
プログラムの流れを図4のようなグラフで表したとき、節と節をつなぐ枝のことを「ストランド(strand)」と呼ぶ。すべてのストランドは並列に実行されることが期待され、また枝と枝が合流する節(cilk_sync;に相当)から開始されるストランドは、その節に合流するすべてのストランドの実行が終了するまで開始されない。
[PageInfo]
LastUpdate: 2010-10-26 20:21:22, ModifiedBy: hiromichi-m
[Permissions]
view:all, edit:login users, delete/config:members