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 つを決めて書きます。
二乗和 |
最大値 |
|
|---|---|---|
各点の値を返すラムダ式( |
|
|
2 つの値をまとめるラムダ式( |
和 |
大きい方 |
初期値( |
|
|
初期値は、まとめても結果を変えない値(単位元)にします。
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 の足す順序は、どちらも差の原因になり得ます。