02. MPI 版:領域を分けて解く
01. 逐次版 のプログラムを、\(P\) ランクで分担する heat1d_mpi に広げます。
領域分割とゴーストセルの交換の考え方は 第 10 章、MPI の関数は 第 8 章 で説明しています。
使うもの
逐次版のものに加えて、次を使います。
もの |
ヘッダ |
|---|---|
|
|
|
|
main の最初で Env env(argc, argv); を作ると MPI が初期化され、main を抜けるときに終了処理が行われます。
作るもの
各ランクの格子
全体のセル数 \(N\) をランク数 \(P\) で割り、各ランクの物理セル数を \(n = N/P\) とします。 \(N\) が \(P\) で割り切れないときは、ランク 0 が理由を表示し、
MPI_Abortで全ランクを終了します。配列は
Array1D<Real> u(n + 2)とu_new(n + 2)です。ランク \(p\) の配列の添字
i(1〜n)は、全体の番号 \(j = p\,n + (i - 1)\) にあたります。 初期条件と誤差の計算では、この \(j\) から \(x_j = (j + 1/2)\Delta x\) を求めます。\(\Delta x\)、\(n_\mathrm{steps}\)、\(\Delta t\)、\(r\) は全体の \(N\) から逐次版と同じ式で求めます。
隣のランク
左隣は
rank - 1、右隣はrank + 1です。ランク 0 の左隣と、ランク \(P - 1\) の右隣は
MPI_PROC_NULLにします。
各ステップ
次の 2 回の
MPI_Sendrecvでゴーストセルを埋めます。型はMPI_DOUBLE、要素数は 1、コミュニケータはmpi::comm()です。呼び出し
送る値 → 相手
受け取る場所 ← 相手
タグ
1 回目
u[1]→ 左隣u[n+1]← 右隣0
2 回目
u[n]→ 右隣u[0]← 左隣1
受信の状態は使わないので、最後の引数には
MPI_STATUS_IGNOREを渡します。左隣が
MPI_PROC_NULLのランクはu[0] = -u[1]を、右隣がMPI_PROC_NULLのランクはu[n+1] = -u[n]を入れます。逐次版と同じ式で、物理セル(
Range1D{1, n + 1})を更新し、uとu_newを入れ替えます。
誤差と出力
各ランクで
reduceにより自分の物理セルの最大誤差を求め、MPI_Allreduce(MPI_MAX)で全体の最大誤差にします。MPI_Gatherで各ランクの物理セル \(n\) 個(&u[1]から)をランク 0 に集めます。ランク 0 は \(N\) 要素の受信用の配列(std::vector<Real>)を用意します。ランク 0 だけが、逐次版と同じ 3 行(1 行目は
N = 64, P = 2, steps = 1024, r = 0.4000のように \(P\) も表示)を表示し、 結果をresult_N<N>_P<P>.txt(たとえばresult_N64_P2.txt)に書き出します。
確かめること
逐次版を実行したのと同じフォルダで、次を実行します。
./build/heat1d_serial 64
for P in 1 2 4; do
mpiexec -np $P ./build/heat1d_mpi 64
cmp result_N64_serial.txt result_N64_P$P.txt
done
表示:\(P = 1, 2, 4\) のどれでも、表示される 2 つの誤差が逐次版と同じ値になります。
ビット単位の一致:
cmpが何も表示しない(ファイルが 1 バイトも違わない)ことを確かめます。割り切れない分割:
mpiexec -np 3 ./build/heat1d_mpi 64で、理由を表示して終了することを確かめます。3 ランク:\(N = 120\) で \(P = 1, 2, 3, 4\) を実行し、すべて逐次版の
result_N120_serial.txtと一致することを確かめます。 このときmax error (exact)は2.941414e-05です。
逐次版と一致しないときは、次の順に調べます。
\(P = 1\) でも違うなら、初期条件の \(x_j\)(全体の番号 \(j\) の求め方)、\(\Delta t\) の求め方を調べます。
\(P = 1\) では一致し、\(P = 2\) で違うなら、
MPI_Sendrecvの送る値・受け取る場所・相手・タグを、上の表と見比べます。端のセルだけ違うなら、物理境界の値を入れる条件(どのランクが入れるか)を調べます。
ファイルのどの行から違うかは、diff result_N64_serial.txt result_N64_P2.txt | head で分かります。
行番号から全体の番号 \(j\)(行番号 − 1)が分かり、どのランクの、どの端の値がおかしいかを絞り込めます。
次へ進む目安
複数のランク数で逐次版とビット単位で一致したら、03. GPU 版 へ進みます。 GPU がなくても、課題 3 の CPU 用ビルドまで進められます。