CilkPlus について

コード書くの疲れたので休憩も兼ねて書きます.

知っている人も多いと思いますが,CilkPlus は並列処理を容易にするため,Intel が自社のコンパイラに用意している C/C++ 用の言語拡張です.
元々は MIT で作られていた C 言語用の拡張だったとかなんだとか.
まあそれはともかく,CilkPlus はキーワードが3つしかないとてもシンプルな設計です.

  • cilk_for
  • cilk_spawn
  • cilk_sync

の3つだけです.
後は reducer などが用意されています.

他には,ベクトル演算を簡略化する Array Notation,関数をベクタライズする Elemental Functions,ループのベクタライズを明示的に許可する #pragma simd などがあります.(これらも CilkPlus の一部だったのか...)

これらの定義は,全て にあります *1

CilkPlus のことについては以下のサイトが良いと思います.
Cilk Home Page | CilkPlus

利用できる環境

現在のところ,Intel コンパイラなら使用可能ですが,それ以外のコンパイラでは GCC は 4.8 で CilkPlus が使えるブランチがあり,4.9 では development branch があります.また LLVM/Clang でも実装されており,github に置かれています.詳しい話は以下.
Which License | CilkPlus

cilk_for

cilk_for は並列 for 文です.OpenMP だと pragma omp parallel for に該当します.

#include <cilk/cilk.h>

template<class T>
void add(std::vector<T> & out, std::vector<T> const& x, std::vector<T> const& y) {
  const std::size_t n = x.size();
  cilk_for(std::size_t i = 0 ; i < n ; ++i) {
    out[i] = x[i] + y[i];
  }
}

cilk_spawn, cilk_sync

cilk_spawn は関数を非同期実行させる命令で,cilk_sync は cilk_spawn で実行した全ての処理が完了するのを待機します.

#include <cilk/cilk.h>

int fib(int n)
{
    if (n < 2) {
        return n;
    }
    int x = cilk_spawn fib(n-1);
    int y = fib(n-2);
    cilk_sync;
    return x + y;
}

reducer

reducer は並行プログラミングでは非常に困る,複数のスレッド間で同期しなければならない計算を助けるための機能です.
例えば総和計算の関数は,全スレッドが参照する総和結果を返す変数をロックしなければいけませんが,reducer はそのようなロックをうまく隠してくれます.

#include <cilk/cilk.h>

template<class T>
T sum(std::vector<T> const& x) {
  cilk::reducer_opadd<T> result; // 自動でゼロ初期化されているらしい.
  cilk_for(std::size_t i = 0 ; i < x.size() ; ++i) {
    result += x[i];
  }
  return result.get_value();
}

終わり

OpenMP と比べると,#ifdef とかない分書きやすいと思います.
CilkPlus はオプションの指定が必要ないため,使える環境ならどこでも使えます.
後,OpenMP ではリダクションの指定が色々制限が強いのですが,CilkPlus なら reducer を自前で実装することで,ユーザ定義型でも使用できるようです.
Tutorial Cilk Plus Reducers | CilkPlus

そういえば OpenMP と CilkPlus の比較記事をあまり見たことがない.
どこかでやっているような気はするんですが...

*1:cilk_for, cilk_spawn, cilk_sync は実際にはマクロで,実際のキーワードは _Cilk_for, _Cilk_spawn, _Cilk_sync なのですが,cilk_for, cilk_spawn, cilk_sync を使うのが推奨されています