02. 縮約:二乗和と最大値

前の課題の設定を使い、reduce で配列の二乗和と最大値を求めます。 第 7 章の総和の例を、二乗和と最大値の 2 つに広げます。

実装する内容

長さ N の配列 x を for_each で x[i] = util::sin(0.001 * i) と初期化し、次の 2 つを求めます。

  • 二乗和 Σ x[i]²

  • 最大値 max x[i]

MISO の reduce は 1 回の呼び出しで 1 つの値を返すので、reduce を 2 回呼びます。 それぞれについて、次の 3 つを決めて書きます。

二乗和

最大値

各点の値を返すラムダ式(map)

x[i] * x[i]

x[i]

2 つの値をまとめるラムダ式(op)

和

大きい方

初期値(init)

0.0

std::numeric_limits<double>::lowest()

初期値は、まとめても結果を変えない値(単位元)にします。 std::numeric_limits には <limits> を読み込みます。 大きい方を選ぶ処理には util::fmax(a, b) を使えます。 util::max2 は double を float に丸めるので(MISO #189)、ここでは使いません。

まず N = 1000、次に N = 1000000 で確かめてください。

結果の確認

x を copy_from でホスト側の配列へ写し、その同じ配列から通常の for 文で期待値を求めます。 最大値は比べて選ぶだけなので、この配列に NaN(数として表せない値)がなければ完全一致で確かめられます。

二乗和は、足す順序によって末尾の桁が変わります。 ホスト側の期待値は、各要素を long double に変換してから二乗し、long double で足します。 long double が double より多い桁を持つかは環境によります。 今回の入力と上の N では、1e-12 * max(1, |期待値|) 以下の差を許容する比較を使い、実際の差も表示してください。 別の入力や大きさへ変えるときは、同じ許容誤差を無条件に使わず、足す回数と丸め誤差を考えて決めます。

CPU で確認できたら、1 次元移流へ進めます。

GPU で実行する段階

第 9 章を読んだ後、CUDA でも同じ課題を実行します。 GPU で作った配列をホスト側へ写し、その配列から求めた期待値と比べます。 CPU と GPU で二乗和の値がどれだけ違うかも記録します。 初期化に使う数学関数の実装と、reduce の足す順序は、どちらも差の原因になり得ます。