2.3.7. MPI_Gatherv¶
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 | #include <mpi.h>
#include <iostream>
#include <vector>
#include <cstdlib>
// 後で定義する関数の前方宣言
void make_send_data(int my_rank, std::vector<double> &send_data);
/*
* 各ノードで作成した、個数が事前には分かっていない配列を
* 1つのノードに集める
*
* 実行方法: mpiexec -n 4 mpi_sample7
*/
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);
// rankごとに送信データを作成する
// データの個数はプロセスごとに違う
std::vector<double> send_data;
make_send_data(my_rank, send_data);
int send_data_count = send_data.size();
// 各rankで作成したデータを表示する
std::cout << "rank: " << my_rank << ", send_data: [";
for (int i = 0; i < send_data_count; i++)
{
std::cout << " " << send_data[i];
}
std::cout << " ]" << std::endl;
std::vector<int> data_counts;
if (my_rank == 0)
{
data_counts.resize(num_procs);
}
// 各ランクで用意したデータの個数を rank == 0 に集める
MPI_Gather(
&send_data_count, // 送信データバッファ
1, // プロセスごとの送信データ数
MPI_INT, // データ型
&data_counts[0], // 受信データバッファ。rank == 0 でのみ使われる
1, // プロセスごとの受信データ数
MPI_INT, // データ型
0, // 受信するrank
MPI_COMM_WORLD
);
// 受信データ総数を計算して、受信バッファを用意する
int total_recv_count = 0;
std::vector<int> recv_data_displacements;
if (my_rank == 0)
{
for (int k = 0; k < num_procs; k++)
{
recv_data_displacements.push_back(total_recv_count);
total_recv_count += data_counts[k];
}
}
std::vector<double> recv_data;
recv_data.resize(total_recv_count);
MPI_Gatherv(
&send_data[0],
send_data_count,
MPI_DOUBLE,
&recv_data[0],
&data_counts[0],
&recv_data_displacements[0],
MPI_DOUBLE,
0,
MPI_COMM_WORLD
);
if (my_rank == 0)
{
// 受信したデータを表示する
std::cout << "rank: 0, recv_data: [" << std::endl;
for (int k = 0; k < num_procs; k++)
{
for (int i = 0; i < data_counts[k]; i++)
{
int j = recv_data_displacements[k] + i;
std::cout << " " << recv_data[j];
}
std::cout << std::endl;
}
std::cout << " ]" << std::endl;
}
MPI_Finalize();
return EXIT_SUCCESS;
}
void make_send_data(int my_rank, std::vector<double> &send_data)
{
srand((unsigned int) my_rank);
/*
* 5以上10未満の乱数
*/
int n = 5 + (rand() % 5);
for (int i = 0; i < n; i++)
{
double val = my_rank * 10 + i;
send_data.push_back(val);
}
}
|
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
]