本プログラムは計算領域をX方向にプロセスの数で分割します。
図3-4-1は計算領域をX方向に4分割したものです。
X方向のセル数Nxがプロセス数Npの倍数でないときはなるべく不均一性が小さくなるように分割します。
各プロセスの必要メモリーはプロセス数分の1になります。
これにより計算ノードの数を増やせばそれに比例して計算できる問題サイズの上限が大きくなります。
MPIでは行列・ベクトル積の計算の前に計算領域の境界で隣のプロセスとの通信が必要になります。
プロセスPは領域境界より左の成分を所有し、プロセスP+1は領域境界上とその右の成分を所有します。
通信する成分は図3-4-1に矢印で示した成分です。
このうち、赤い成分はプロセスPからプロセスP+1の方向に通信し、
青い成分はプロセスP+1からプロセスPの方向に通信します。
これによりプロセスP,P+1ともに自分の所有する成分を更新することができます。
リスト3-4-1にMPIにより電界を通信するプログラムを示します。
アドレスと通信量は予め計算し、バッファーも予め確保しています。
void comm_boundary(d_complex_t *v) { #ifdef MPI MPI_Status status; const int tag = 0; // -X direction if (commRank > 0) { // copy to send buffer memcpy(Sendcomplex_m, &v[Send0_m], Sendcount_m * sizeof(d_complex_t)); for (int64_t n = 0; n < Sendcount_m; n++) { Sendbuf_m[(2 * n) + 0] = Sendcomplex_m[n].r; Sendbuf_m[(2 * n) + 1] = Sendcomplex_m[n].i; } // MPI int dst = commRank - 1; MPI_Sendrecv(Sendbuf_m, 2 * Sendcount_m, MPI_DOUBLE, dst, tag, Recvbuf_m, 2 * Recvcount_m, MPI_DOUBLE, dst, tag, MPI_COMM_WORLD, &status); // copy from recv buffer for (int64_t n = 0; n < Recvcount_m; n++) { Recvcomplex_m[n].r = Recvbuf_m[(2 * n) + 0]; Recvcomplex_m[n].i = Recvbuf_m[(2 * n) + 1]; } memcpy(&v[Recv0_m], Recvcomplex_m, Recvcount_m * sizeof(d_complex_t)); } // +X direction if (commRank < commSize - 1) { // copy to send buffer memcpy(Sendcomplex_p, &v[Send0_p], Sendcount_p * sizeof(d_complex_t)); for (int64_t n = 0; n < Sendcount_p; n++) { Sendbuf_p[(2 * n) + 0] = Sendcomplex_p[n].r; Sendbuf_p[(2 * n) + 1] = Sendcomplex_p[n].i; } // MPI int dst = commRank + 1; MPI_Sendrecv(Sendbuf_p, 2 * Sendcount_p, MPI_DOUBLE, dst, tag, Recvbuf_p, 2 * Recvcount_p, MPI_DOUBLE, dst, tag, MPI_COMM_WORLD, &status); // copy from recv buffer for (int64_t n = 0; n < Recvcount_p; n++) { Recvcomplex_p[n].r = Recvbuf_p[(2 * n) + 0]; Recvcomplex_p[n].i = Recvbuf_p[(2 * n) + 1]; } memcpy(&v[Recv0_p], Recvcomplex_p, Recvcount_p * sizeof(d_complex_t)); } #endif }
表3-4-1と図3-4-3にプロセス数と計算時間の関係を示します。
OpenMPと同じくプロセス数が少ないときはmatrixモードの方が速いですが、
40プロセスでほぼ同じになります。
プロセス数 | benchmark100 | benchmark200 | benchmark300 | |||
---|---|---|---|---|---|---|
nomatrix | matrix | nomatrix | matrix | nomatrix | matrix | |
1 | 194秒 (1.0) | 142秒 (1.0) | 1495秒 (1.0) | 1111秒 (1.0) | 6273秒 (1.0) | 3674秒 (1.0) |
2 | 99秒 (2.0) | 74秒 (1.9) | 772秒 (1.9) | 586秒 (1.9) | 3085秒 (2.0) | 1938秒 (1.9) |
4 | 50秒 (3.8) | 39秒 (3.6) | 401秒 (3.7) | 304秒 (3.7) | 1575秒 (4.0) | 1004秒 (3.7) |
10 | 28秒 (6.9) | 22秒 (6.3) | 207秒 (7.2) | 168秒 (6.6) | 767秒 (8.2) | 516秒 (7.1) |
20 | 27秒 (7.1) | 24秒 (5.9) | 166秒 (9.0) | 149秒 (7.4) | 568秒 (11.0) | 446秒 (8.2) |
40 | 36秒 (5.3) | 34秒 (4.1) | 178秒 (8.4) | 172秒 (6.5) | 495秒 (12.7) | 483秒 (7.6) |