レデューサーの動作

レデューサーのメカニズムとセマンティクスの説明は、プログラミングの上級者向けに、レデューサーの使用規則の理解に役立ち、カスタム・レデューサーを記述するのに必要な情報を提供します。

レデューサーを最も簡単に表現すると、値、単位元、リダクション関数を持つオブジェクトといえます。

レデューサー・ライブラリーで提供されているレデューサーには、安全に一貫した方法で使用できるように、インターフェイスが用意されています。

ここでは、レデューサー宣言時に作成されるオブジェクトを、レデューサーの "左端" のインスタンスと呼びます。

次のセクションでは、簡単な例を使用して、プログラムを実行したときのシステムのランタイム動作について説明します。

最初に、スチールがある場合とない場合の cilk_spawn の実行例について考えてみます。 この例では、レデューサーの動作は単純です。

次の図は、この動作を表したものです。

スチールが発生しない場合

(A) として示されている cilk_spawn の後にスチールはありません。



この場合、ストランド (2) と (3) は、ストランド (1) のレデューサー・オブジェクトを直接更新できます。スチールが発生しないため、新しいビューは作成されず、reduce 操作も呼び出されません。

スチールが発生する場合

(A) の cilk_spawn の継続処理であるストランド (2) がスチールされます。



この場合、子であるストランド (3) は、ストランド (1) のレデューサー・オブジェクトを参照することができます。継続処理であるストランド (2) は、単位元とともに新しいビューを受け取ります。同期ポイント (B) で、レデューサーの新しいビューはストランド (3) のオリジナルのビューへマージされます。

例: reducer_opadd<> の使用

この例は、reducer_opadd<> を使用して整数値の合計を並列に計算する単純なプログラムです。 単位元は 0 で、リダクション関数は左の値に右の値を加算します。

1 #include <cilk/cilk.h>
2 #include <cilk/reducer_opadd.h>
3
4    cilk::reducer_opadd<int> sum;
5
6    void addsum()
7    {
8      sum += 1;
9    }
10
11    int main()
12    {
13      sum += 1;
14      cilk_spawn addsum();
15      sum += 1; // the value of sum here depends on whether a steal occurred
16      cilk_sync;
17      return sum.get_value();
18    }

最初に、シングルコア・プロセッサーで実行した場合について考えてみます。この場合、スチールは発生しません。そのため、sum のプライベート・ビューは作成されず、すべての演算は左端のインスタンスに対して実行されます。 新しいビューが作成されないため、リダクション操作が呼び出されることはありません。sum の値は 0 から最終値である 3 まで単調に増えます。

この例では、スチールが発生しないため、cilk_sync 文は何もしない操作 (no-op) として処理されます。

次に、スチールが発生する場合について考えてみます。15 行目の sum へのアクセス時に、単位元 (0) を持つ新しいビューが作成されます。 15 行目が実行された後の sum の値は 1 になります。 親が (単位元を持つ) 新しいビューを取得し、子はスポーン時にアクティブであったビューを取得します。これにより、レデューサーは、可換ではありませんが結合側を満たす reduce 操作によって決定性のある結果を確保します。子 (addsum) は左端のインスタンスを使用するため、8 行目の sum は 1 から 2 に増加します。

cilk_sync 文に到達した際に、それぞれのストランドは異なる sum のビューを保持しているため、リダクション操作を使用してビューがマージされます。 この例では、親の新しいビュー (値 1) が子の保持するビュー (値 2) に加算され、左端のインスタンスの値が 3 になります。リダクション後に新しいビューは破棄されます。

Lazy セマンティクス

各ストランドがレデューサーのプライベート・ビューを保持していると考えることができます。パフォーマンスの観点から、これらのビューは次の 2 つの条件を満たす場合にのみ作成されます。

新しいビューが作成されると、cilk_sync で以前のビューにマージされます。 ビューが作成されない場合、リダクションは必要ありません。(論理的には、単位元が作成されマージされる、何もしない操作 (no-op) と見なせます。)

安全な操作

レデューサーは、単位元とリダクション関数を実装するだけでも定義することができます。ただし、安全性と便宜上のため、レデューサー・ライブラリーで提供されているレデューサーはクラスでラップされ、ビュータイプの演算サブセットのみ提供されます。

例えば、reducer_opadd には +=、-=、++、--、+、- 演算子があります。 乗算 (演算子 *) などの演算は、乗算と加算では結合則が満たされないため (つまり、(a+b)*ca+(b*c) と同じではないため)、reducer_opadd ではサポートされていません。 reducer_opadd で乗算を実行しようとすると、コンパイル時エラーが発生します。


このヘルプトピックについてのフィードバックを送信