この例は、並列に合計を計算するコードにレデューサーを使用する方法を示します。 次のようなシリアルプログラムについて考えてみます。この例は、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;
}
シリアルコードへの次の変更は、レデューサーの使用方法を示します。
適切なレデューサー・ヘッダー・ファイルをインクルードします。
リダクション変数を TYPE ではなく、reducer_kind<TYPE> として宣言します。
プログラムを並列化します。ここでは、for ループを cilk_for ループに変更しています。
cilk_for ループが完了した後に、get_value() メソッドを使用して、レデューサーの最終値を取得します。