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

レデューサーの使用 - 簡単な例

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

この例は、並列に合計を計算するコードにレデューサーを使用する方法を示します。次のようなシリアルプログラムについて考えてみます。この例は、compute() 関数を繰り返し呼び出し、その結果を total 変数に累積します。

#include <iostream>

unsigned int compute(unsigned int i)
{
   return i; // i から計算した値を返す
}

int main(int argc, char* argv[])
{
   unsigned long long int n = 1000000;
   unsigned long long int total = 0;

   // 整数 1..n の合計を計算する
   for(unsigned int i = 1; i <= n; ++i)
   {
     total += compute(i);
   }

   // 最初の n 個の整数の合計は n * (n+1) / 2
   unsigned long long int correct = (n * (n+1)) / 2;

   if (total == correct)
     std::cout << "Total (" << total
               << ") is correct" << std::endl;
   else
     std::cout << "Total (" << total
               << ") is WRONG, should be "
               << correct << std::endl;
   return 0;
}

このプログラムをインテル® Cilk™ Plus プログラムに変換します。for ループを cilk_for に変更してループが並列に実行されるようにすると、total 変数でデータ競合が発生します。このデータ競合は、total をレデューサーにすることで解決できます。ここでは、結合則を満たす + 演算子を持つ型のために定義されている reducer<op_add> というレデューサーを使用しています。変更後のプログラムは次のようになります。

#include <cilk/cilk.h>
#include <cilk/reducer_opadd.h>
#include <iostream>

unsigned int compute(unsigned int i)
{
   return i; // i から計算した値を返す
}
int  main(int argc, char* argv[])
{
   unsigned long long int n = 1000000;
   cilk::reducer< cilk::op_add<unsigned long long int> > total (0);

   // 1..n の合計を計算する
   cilk_for(unsigned int i = 1; i <= n; ++i)
   {
     *total += compute(i);
   }

   // 最初の N 個の整数の合計は n * (n+1) / 2
   unsigned long long int correct = (n * (n+1)) / 2;

   if ( total.get_value() == correct)
     std::cout << "Total (" <<  total.get_value()
               << ") is correct" << std::endl;
   else
     std::cout << "Total (" <<  total.get_value()
               << ") is WRONG, should be "
               << correct << std::endl;
   return 0;
}

シリアルコードへの次の変更は、レデューサーの使用方法を示します。

  1. 適切なレデューサー・ヘッダー・ファイル (cilk/reducer_opadd.h) をインクルードします。

  2. リダクション変数を TYPE ではなく、reducer< op_kind<TYPE> > として宣言します。

  3. プログラムを並列化します。ここでは、for ループを cilk_for ループに変更しています。

  4. 並列コードでは、オリジナルの変数への参照をレデューサー変数の逆参照 (*total) に変更します。

  5. すべての並列ストランドが同期した後にレデューサーの最終値を取得します。ここでは、cilk_for ループが完了した後に total.get_value() を実行しています。

レデューサーは、コピーまたは割り当てできない C++ クラス・オブジェクトです。