2.3.6. MPI_Scatterv¶
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 | #include <mpi.h>
#include <iostream>
#include <vector>
#include <cstdlib>
/*
* rank == 0 から各プロセスにまちまちの長さの配列データを送信する
* 受信側は事前に要素数が分かっていない。
*
* 実行方法: mpiexec -n 4 mpi_sample6
*/
// 後で定義する関数の前方宣言
void make_send_data(int dest_rank, std::vector<double> &send_buffer,
std::vector<int> &data_count_array, std::vector<int> &send_data_displacements);
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で定義するが、
// 中身をつめるのは root rank のみ
std::vector<double> send_data;
std::vector<int> data_counts;
std::vector<int> send_data_displacements;
// rank == 0 では全プロセス向けの送信データを作成する
if (my_rank == 0)
{
for (int k = 0; k < num_procs; k++)
{
make_send_data(k, send_data, data_counts, send_data_displacements);
}
// できたデータを表示する
std::cout << "rank: 0, send_data: [" << std::endl;
for (int k = 0; k < num_procs; k++)
{
for (int i = 0; i < data_counts[k]; i++)
{
int j = send_data_displacements[k] + i;
std::cout << " " << send_data[j];
}
std::cout << std::endl;
}
std::cout << " ]" << std::endl;
}
// 各rank向けのデータの個数を全rankに伝えるために
// MPI_Scatterを使う。「個数」という int 値を1要素ずつ
// 送ればよいので、受信バッファはスカラーのintにする
int recv_data_count;
MPI_Scatter(
&data_counts[0], // 送信バッファのアドレス。std::vectorの先頭要素のアドレス
1, // 各プロセスに送るデータ数。vectorの要素数ではない点に注意
MPI_INT, // 送信データの型
&recv_data_count, // 受信バッファのアドレス
1, // 受信するデータ数
MPI_INT, // 受信データの型
0, // 送信元rank
MPI_COMM_WORLD
);
// 全プロセスで受信予定のデータ数が分かったので、配列データの受信バッファを設ける
std::vector<double> recv_data(recv_data_count, 0);
MPI_Scatterv(
&send_data[0], // 送信データの先頭アドレス(rank == 0 でのみ利用される)
&data_counts[0], // 各rank向けに送り出す要素数が入った配列
&send_data_displacements[0], // 各rank向けのデータが配列のどの位置から始まるかが入った配列
MPI_DOUBLE, // 送信データの型
&recv_data[0], // 各rankでの受信データバッファ
recv_data_count, // 各rankでの受信データ数
MPI_DOUBLE, // 受信データの型
0, // 送信元とするrank
MPI_COMM_WORLD
);
// 各rankで受信できたデータを表示する
std::cout << "rank: " << my_rank << ", recv_data: [";
for (int i = 0; i < recv_data_count; i++)
{
std::cout << " " << recv_data[i];
}
std::cout << " ]" << std::endl;
MPI_Finalize();
return EXIT_SUCCESS;
}
void make_send_data(int dest_rank, std::vector<double> &send_data,
std::vector<int> &send_data_counts, std::vector<int> &send_data_displacements)
{
int current_buffer_length = send_data.size();
send_data_displacements.push_back(current_buffer_length);
/*
* 5以上10未満の乱数で送信データ数を決める
*/
int n = 5 + (rand() % 5);
send_data_counts.push_back(n);
/* 決めた個数のデータを送信バッファに入れる。*/
for (int i = 0; i < n; i++)
{
/* 値は 10 * rank + rankごとの通し番号 */
double val = dest_rank * 10 + i;
send_data.push_back(val);
}
}
|
MPI_Scatterv は、MPI_Scatter と似て、配列データをルートから各rankに分配しますが、 分配の個数を宛先rankごとに独立に指定します。
送られてくるデータの個数が実行時に決まり、受信側では分からないような場合には、 データ本体を送るのに先立って、データの個数を送信側から受信側に伝える必要があります。 そのために、このサンプルでは一旦 MPI_Scatter を使って送信データ数だけを各rankに 伝達した後で、 MPI_Scatterv を使ってデータ本体を送っています。
各rankに送るデータの個数は、ルートrankにおいて乱数で決めています。
受信側では伝えられた個数に合わせて受信バッファの大きさを調整します。
openmpiによる実行例を示します。:
$ mpic++ -o mpi_sample6 mpi_sample6.cpp
$ mpiexec -n 4 mpi_sample6
rank: 0, send_data: [
0 1 2 3 4 5 6 7
10 11 12 13 14 15
20 21 22 23 24 25 26
30 31 32 33 34
]
rank: 0, recv_data: [ 0 1 2 3 4 5 6 7 ]
rank: 1, recv_data: [ 10 11 12 13 14 15 ]
rank: 2, recv_data: [ 20 21 22 23 24 25 26 ]
rank: 3, recv_data: [ 30 31 32 33 34 ]