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

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

#include <iostream>

unsigned int compute(unsigned int i)
{
   return i; // return a value computed from i
}

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

   // Compute the sum of integers 1..n
   for(unsigned int i = 1; i <= n; ++i)
   {
     total += compute(i);
   }

   // the sum of the first n integers should be n * (n+1) / 2
   unsigned 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_opadd というレデューサーを使用しています。 変更後のプログラムは次のようになります。

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

unsigned int compute(unsigned int i)
{
   return i; // return a value computed from i
}
int  main(int argc, char* argv[])
{
   unsigned int n = 1000000;
   cilk::reducer_opadd<unsigned int> total;

   // Compute 1..n
   cilk_for(unsigned int i = 1; i <= n; ++i)
   {
     total += compute(i);
   }

   // the sum of the first N integers should be n * (n+1) / 2
   unsigned 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. 適切なレデューサー・ヘッダー・ファイルをインクルードします。

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

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

  4. cilk_for ループが完了した後に、get_value() メソッドを使用して、レデューサーの最終値を取得します。

レデューサーはオブジェクトです。そのため、値を直接コピーすることはできません。 memcpy() を使用してレデューサー・オブジェクトをコピーすると、予期しない結果を引き起こします。 代わりに、コピー・コンストラクターを使用してください。


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