2.4.7. MPI_Gatherv
1#include <mpi.h>
2
3#include <iostream>
4#include <vector>
5#include <cstdlib>
6
7// 後で定義する関数の前方宣言
8void make_send_data(int my_rank, std::vector<double> &send_data);
9
10/*
11 * 各ノードで作成した、個数が事前には分かっていない配列を
12 * 1つのノードに集める
13 *
14 * 実行方法: mpiexec -n 4 mpi_sample7
15 */
16int main(int argc, char *argv[])
17{
18 MPI_Init(&argc, &argv);
19
20 int num_procs;
21 int my_rank;
22
23 MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
24 MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
25
26 // rankごとに送信データを作成する
27 // データの個数はプロセスごとに違う
28 std::vector<double> send_data;
29 make_send_data(my_rank, send_data);
30 int send_data_count = send_data.size();
31
32 // 各rankで作成したデータを表示する
33 std::cout << "rank: " << my_rank << ", send_data: [";
34 for (int i = 0; i < send_data_count; i++)
35 {
36 std::cout << " " << send_data[i];
37 }
38 std::cout << " ]" << std::endl;
39
40 std::vector<int> data_counts;
41
42 if (my_rank == 0)
43 {
44 data_counts.resize(num_procs);
45 }
46
47 // 各ランクで用意したデータの個数を rank == 0 に集める
48 MPI_Gather(
49 &send_data_count, // 送信データバッファ
50 1, // プロセスごとの送信データ数
51 MPI_INT, // データ型
52 &data_counts[0], // 受信データバッファ。rank == 0 でのみ使われる
53 1, // プロセスごとの受信データ数
54 MPI_INT, // データ型
55 0, // 受信するrank
56 MPI_COMM_WORLD
57 );
58
59 // 受信データ総数を計算して、受信バッファを用意する
60 int total_recv_count = 0;
61 std::vector<int> recv_data_displacements;
62
63 if (my_rank == 0)
64 {
65 for (int k = 0; k < num_procs; k++)
66 {
67 recv_data_displacements.push_back(total_recv_count);
68 total_recv_count += data_counts[k];
69 }
70 }
71
72 std::vector<double> recv_data;
73 recv_data.resize(total_recv_count);
74
75 MPI_Gatherv(
76 &send_data[0],
77 send_data_count,
78 MPI_DOUBLE,
79 &recv_data[0],
80 &data_counts[0],
81 &recv_data_displacements[0],
82 MPI_DOUBLE,
83 0,
84 MPI_COMM_WORLD
85 );
86
87 if (my_rank == 0)
88 {
89 // 受信したデータを表示する
90 std::cout << "rank: 0, recv_data: [" << std::endl;
91 for (int k = 0; k < num_procs; k++)
92 {
93 for (int i = 0; i < data_counts[k]; i++)
94 {
95 int j = recv_data_displacements[k] + i;
96 std::cout << " " << recv_data[j];
97 }
98 std::cout << std::endl;
99 }
100 std::cout << " ]" << std::endl;
101 }
102
103 MPI_Finalize();
104 return EXIT_SUCCESS;
105}
106
107void make_send_data(int my_rank, std::vector<double> &send_data)
108{
109 srand((unsigned int) my_rank);
110 /*
111 * 5以上10未満の乱数
112 */
113 int n = 5 + (rand() % 5);
114 for (int i = 0; i < n; i++)
115 {
116 double val = my_rank * 10 + i;
117 send_data.push_back(val);
118 }
119}
MPI_Gatherv では、rankごとに個別に指定した個数の配列データを ルートrankに用意した全rank分の配列に格納します。
送信側と受信側の双方で転送するデータの個数を指定する必要が あるので、必要に応じて MPI_Gatherv の呼びたしに先立って 個数の情報を通信で伝達します。
この例では各rankで乱数によりデータの個数を決めており、その後で MPI_Gather を使って、全rankの個数情報をルートrankに集めています。 その後でMPI_Gatherv を使ってデータ本体を転送しています。
openmpiによる実行例を示します。:
$ mpic++ -o mpi_sample7 mpi_sample7.cpp
$ mpiexec -n 4 mpi_sample7
rank: 0, send_data: [ 0 1 2 3 4 5 6 7 ]
rank: 1, send_data: [ 10 11 12 13 14 15 16 17 ]
rank: 2, send_data: [ 20 21 22 23 24 ]
rank: 3, send_data: [ 30 31 32 33 34 35 ]
rank: 0, recv_data: [
0 1 2 3 4 5 6 7
10 11 12 13 14 15 16 17
20 21 22 23 24
30 31 32 33 34 35
]