インテル® C++ コンパイラー 19.0 デベロッパー・ガイドおよびリファレンス
インテル® Cilk™ Plus は古い機能 (非推奨) です。代わりに、OpenMP* またはインテル® TBB を使用してください。詳細は、「インテル® Cilk™ Plus の代わりに OpenMP* またはインテル® TBB を使用するためのアプリケーションの移行」 (英語) を参照してください。
プログラムを開発する際、安全性、正当性、パフォーマンスに関する次の考察に留意してください。
レデューサーとビュー
レデューサーはビューのインスタンスを管理します。並列リダクション処理のコードは、現在の並列ストランド用にビュー・インスタンスを更新します。現在のビュー・インスタンスには、レデューサーを逆参照することでアクセスできます (例: *r = *r OP a)。
つまり、レデューサーはビューへのポインターとして考えることができます。
安全性
前述のように適切に記述されたレデューサーを使用すると、異なる並列ストランド間によるレデューサーのビューへのアクセスにおいてデータ競合は発生しません。
浮動小数点演算
浮動小数点算術演算では、コンピューターの浮動小数点数の表現方法により、オーバーフロー、アンダーフロー、そして精度の低下が発生するため、厳密には結合則を満たしていません。これは、レデューサーを含む、浮動小数点演算を再結合する最適化に影響する可能性があります。
浮動小数点型のレデューサーを使用する前に、データの特性と結果への影響を慎重に検証してください。
初期値と最終値
レデューサーの初期値は、レデューサーのコンストラクターへの引数として、またはレデューサーの set_value() 関数を呼び出して設定できます。初期値を指定しない場合、レデューサーの単位元が使用されます。
計算の最終値は、計算が終了した後に get_value() 関数を呼び出して取得できます。
あるいは、変数の値をレデューサーに移動したり (変数は未定義のまま、レデューサーの move_in() 関数を呼び出す)、レデューサーの値を変数に移動 (レデューサーの値は未定義のまま、レデューサーの move_out() 関数を呼び出す) することができます。
get_value()、set_value()、move_in()、move_out() は、常に現在のストランドのビュー・インスタンスにアクセスします。これらの関数を並列処理中に呼び出すことはできますが、ほとんどの場合意味がありません。レデューサーの初期化には set_value() と move_in() のみを使用し、最終値の取得には get_value() と move_out() のみを使用すべきです。
許可される操作
リダクション操作が結合則を満たしているため、レデューサーは並列処理で正しい結果 (つまり、シリアル処理と同じ結果) を計算します。結合則を満たしていない処理の結合は意図しない結果につながります。つまり、レデューサーのビューで許可される変更は、リダクション操作による更新 (view = view OP value) またはセマンティクス的に等価な操作のみです。(例えば、加算レデューサーでは次の操作が許可されます: view = view + value、view += value (view = view + value と等価)、view++ (view = view + 1 と等価)、および view = view - value (view = view + (- value) と等価)。)
すべてのライブラリー・レデューサー・クラス・ビューには次の制約があります: view が加算レデューサーのビューである場合、view = view * value または view = 3 はコンパイルされません。カスタム・レデューサー・クラスにも同様の制約がある場合があります。ビューで許可される操作が構文で制約されていないレデューサー・クラスを使用する場合、演算子の制約を理解し、それに従うのは開発者の責任です。
パフォーマンス
レデューサーを適切に使用すると、ランタイムのパフォーマンス低下はほとんど、あるいはまったくありません。ただし、パフォーマンスについては、以下のことを考慮しなければなりません。
レデューサーの効率は、ビューの作成とマージにはオーバーヘッドがほとんどかからないという仮定に基づいています。単位元と reduce 操作の実行時間が小さく一定でない場合、ビューの管理に伴うオーバーヘッドにより並列実行の利点は失われます。
新しいビュー・インスタンスが作成されたかどうかに関係なく、すべてのレデューサーにおいてストランドが同期される cilk_sync でオーバーヘッドが発生します。多数のレデューサー (例えば、大きなレデューサーの配列) を使用する場合は、これが重要になることがあります。