2.3.10. 全隣接rank同士の相互通信¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | #include <mpi.h>
#include <iostream>
#include <cstdlib>
#include <vector>
/*
* rank番号が自分の前後のプロセスと相互に送受信する。
*
* より大きいrankに向けてデータを送るのを「右向き」
* より小さいrankに向けてデータを送るのを「左向き」
* と呼ぶことにする。
*
* 実行方法: mpiexec -n 4 mpi_sample10
*/
int main(int argc, char *argv[])
{
MPI_Init(&argc, &argv);
int num_procs;
int my_rank;
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
int send_data_count = 10;
std::vector<double> send_left_data(send_data_count, 0);
std::vector<double> send_right_data(send_data_count, 0);
// それぞれのrankで送信するデータを用意する
for (int i = 0; i < send_data_count; i++)
{
// 左向きには負の値のデータを送る
send_left_data[i] = - ((my_rank * 10) + i);
send_right_data[i] = (my_rank * 10) + i;
}
// 送信データを表示する
std::cout << "rank: " << my_rank << ", send_left_data: [";
for (int i = 0; i < send_data_count; i++)
{
std::cout << " " << send_left_data[i];
}
std::cout << " ]" << std::endl;
std::cout << "rank: " << my_rank << ", send_right_data: [";
for (int i = 0; i < send_data_count; i++)
{
std::cout << " " << send_right_data[i];
}
std::cout << " ]" << std::endl;
int recv_data_count = 10;
std::vector<double> recv_left_data(recv_data_count, 0); // 左向きの受信データ(右のノードから来たデータ)
std::vector<double> recv_right_data(recv_data_count, 0); // 右向きの受信データ(左ノードから来たデータ)
int left_rank = (my_rank - 1 + num_procs) % num_procs;
int right_rank = (my_rank + 1 + num_procs) % num_procs;
// ノンブロッキングの send/recv を使って、相互にデータを送受信する
// 互いに同時に送信しても問題ない
MPI_Request requests[4];
MPI_Status statuses[4];
// 左向きに送信
MPI_Isend(
&send_left_data[0],
send_data_count,
MPI_DOUBLE,
left_rank, // 送り先のrank
1, // 左向きのデータにはタグとして 1 を割り当てることにする
MPI_COMM_WORLD,
&requests[0] // このノンブロッキング操作に対して発行されるIDをrequestsに受け取る
);
// 右向きに送信
MPI_Isend(
&send_right_data[0],
send_data_count,
MPI_DOUBLE,
right_rank, // 送り先のrank
2, // 右向きのデータにはタグとして 2 を割り当てることにする
MPI_COMM_WORLD,
&requests[1] // このノンブロッキング操作に対して発行されるIDをrequestsに受け取る
);
// 左向きのデータを受信
MPI_Irecv(
&recv_left_data[0],
recv_data_count,
MPI_DOUBLE,
right_rank, // 送り元のrank。左向きデータは右からやってくる
1, // タグ。左向きデータなので 1
MPI_COMM_WORLD,
&requests[2] // このノンブロッキング操作に対して発行されるIDを記録する
);
// 右向きのデータを受信
MPI_Irecv(
&recv_right_data[0],
recv_data_count,
MPI_DOUBLE,
left_rank, // 送り元のrank。右向きデータは左からやってくる
2, // タグ。右向きデータなので 2
MPI_COMM_WORLD,
&requests[3] // このノンブロッキング操作に対して発行されるIDを記録する
);
// ノンブロッキング操作が完了するまで待つ。
// どの操作が完了するまで待つのかを、MPI_Request の配列で指定する。
// 操作ごとに失敗する可能性があるので、操作ごとの実行結果をMPI_Statusの
// 配列で受け取る
MPI_Waitall(
4, // 操作の数
&requests[0],
&statuses[0]
);
// 受信できたデータを表示する
std::cout << "rank: " << my_rank << ", recv_left_data: [";
for (int i = 0; i < recv_data_count; i++)
{
std::cout << " " << recv_left_data[i];
}
std::cout << " ]" << std::endl;
std::cout << "rank: " << my_rank << ", recv_right_data: [";
for (int i = 0; i < recv_data_count; i++)
{
std::cout << " " << recv_right_data[i];
}
std::cout << " ]" << std::endl;
MPI_Finalize();
return EXIT_SUCCESS;
}
|
MPI_Isend と MPI_Irecv を使ったさらに踏み込んだ例として、 任意の数のプロセス数で起動された際に、前後のrankと相互に データを送り合う例を示します。
openmpiでの実行例を示します。:
$ mpic++ -o mpi_sample10 mpi_sample10.cpp
$ mpiexec -n 4 mpi_sample10
rank: 2, send_left_data: [ -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 ]
rank: 2, send_right_data: [ 20 21 22 23 24 25 26 27 28 29 ]
rank: 0, send_left_data: [ 0 -1 -2 -3 -4 -5 -6 -7 -8 -9 ]
rank: 0, send_right_data: [ 0 1 2 3 4 5 6 7 8 9 ]
rank: 1, send_left_data: [ -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 ]
rank: 3, send_left_data: [ -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 ]
rank: 1, send_right_data: [ 10 11 12 13 14 15 16 17 18 19 ]
rank: 3, send_right_data: [ 30 31 32 33 34 35 36 37 38 39 ]
rank: 0, recv_left_data: [ -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 ]
rank: 0, recv_right_data: [ 30 31 32 33 34 35 36 37 38 39 ]
rank: 1, recv_left_data: [ -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 ]
rank: 3, recv_left_data: [ 0 -1 -2 -3 -4 -5 -6 -7 -8 -9 ]
rank: 3, recv_right_data: [ 20 21 22 23 24 25 26 27 28 29 ]
rank: 2, recv_left_data: [ -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 ]
rank: 2, recv_right_data: [ 10 11 12 13 14 15 16 17 18 19 ]
rank: 1, recv_right_data: [ 0 1 2 3 4 5 6 7 8 9 ]