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