2.3.11. MPI_Type_create_struct¶
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 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 | #include <mpi.h>
#include <iostream>
#include <iomanip>
#include <vector>
#include <cstdlib>
#include <cmath>
/*
* 自作のクラスをMPIにデータ型として登録して、
* クラスの配列を送受信する
*
*/
/*
* 船の座標と方位、速度を保持する構造体(全てがpublicなclass)
* BoatData には変数が 4 個あり、型は int, float[2], double, double
*
*/
struct BoatData {
int boat_id_; // 船の個体番号
float pos_[2]; // 座標
double heading_; // 進行方向の方位
double vel_; // 速度
};
/*
* MPI に型を登録して得られたコードを保持する変数
*/
MPI_Datatype g_boat_data_type; // グローバル変数なので "g_"
void register_type();
void send_data();
void recv_data();
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);
if (num_procs != 2) {
std::cerr << "Run this program in MPI with 2 processes." << std::endl;
return EXIT_FAILURE;
}
register_type();
if (my_rank == 0)
{
send_data();
}
else
{
recv_data();
}
MPI_Finalize();
return EXIT_SUCCESS;
}
void register_type()
{
// 各変数を配列と見た時の、それぞれの配列の長さ
int block_length[4] = { 1, 2, 1, 1};
// 構造体の先頭からの変位(バイト数)
// 長さの違う変数の間にはコンパイラが隙間を開ける(パディングという)ことがある
// ので、マクロを使って値を取得する
MPI_Aint displacements[4] = {
offsetof(BoatData, boat_id_),
offsetof(BoatData, pos_),
offsetof(BoatData, heading_),
offsetof(BoatData, vel_)
};
// 各変数の型
MPI_Datatype array_of_types[4] = {
MPI_INT,
MPI_FLOAT,
MPI_DOUBLE,
MPI_DOUBLE
};
// 以上のデータからstructの構造をMPIに登録して、コードを割り当ててもらう。
// このデータを送受信する全てのプロセスでそれぞれ登録する必要がある。
MPI_Type_create_struct(
4,
&block_length[0],
&displacements[0],
&array_of_types[0],
&g_boat_data_type
);
MPI_Type_commit(&g_boat_data_type);
}
/*
* rank 0 にて、rank 1 向けにデータを送信する
*/
void send_data()
{
std::vector<BoatData> send_data;
send_data.resize(3);
// データ型の設定を間違えると変数の中身が一部しか送られなくなるので
// 確認のため、有効桁に非ゼロの値がたくさん入ったデータを送る
send_data[0].boat_id_ = 0;
send_data[0].pos_[0] = 1.010101010101010101; // pos は float
send_data[0].pos_[1] = 1.010101010101010101;
send_data[0].heading_ = 0.0/4.0 * M_PI;
send_data[0].vel_ = 1.010101010101010101; // vel は double
send_data[1].boat_id_ = 1;
send_data[1].pos_[0] = 2.020202020202020202;
send_data[1].pos_[1] = 2.020202020202020202;
send_data[1].heading_ = 1.0/4.0 * M_PI;
send_data[1].vel_ = 2.020202020202020202;
send_data[2].boat_id_ = 2;
send_data[2].pos_[0] = 3.030303030303030303;
send_data[2].pos_[1] = 3.030303030303030303;
send_data[2].heading_ = 2.0/4.0 * M_PI;
send_data[2].vel_ = 3.030303030303030303;
std::cout.precision(18); // double の有効桁数より少し多め
std::cout << "rank: 0, send_data: [" << std::endl;
for (size_t i = 0; i < send_data.size(); i++)
{
std::cout << "{ boat_id: " << send_data[i].boat_id_;
std::cout << ", pos: [ " << send_data[i].pos_[0] << " " << send_data[i].pos_[1] << " ]";
std::cout << ", heading: " << send_data[i].heading_;
std::cout << ", vel: " << send_data[i].vel_;
std::cout << "}" << std::endl;
}
std::cout << "]" << std::endl;
MPI_Send(
&send_data[0], // 送信データバッファ
send_data.size(), // 送信要素数
g_boat_data_type, // 要素の型コード
1, // 送信先のrank
1, // タグ。送り側と受け側で同じ値を指定する。値は自分で決める。
MPI_COMM_WORLD
);
}
/*
* rank 1 にて、rank 0 からデータを受信する。
*/
void recv_data()
{
std::vector<BoatData> recv_data;
recv_data.resize(3);
MPI_Status status;
MPI_Recv(
&recv_data[0], // 受信データバッファ
recv_data.size(),
g_boat_data_type,
0, // 送信元のrank
1, // タグ
MPI_COMM_WORLD,
&status
);
std::cout.precision(18);
std::cout << "rank: 1, recv_data: [" << std::endl;
for (size_t i = 0; i < recv_data.size(); i++)
{
std::cout << "{ boat_id: " << recv_data[i].boat_id_;
std::cout << ", pos: [ " << recv_data[i].pos_[0] << " " << recv_data[i].pos_[1] << " ]";
std::cout << ", heading: " << recv_data[i].heading_;
std::cout << ", vel: " << recv_data[i].vel_;
std::cout << "}" << std::endl;
}
std::cout << "]" << std::endl;
}
|
自分で定義した構造体またはクラスにデータを保持させて、その構造体の配列を データとして送信したい場合があります。そのような時に便利なのがMPIに 自作のデータ型を登録する機能です。
これを使うには、構造体のメンバ変数の登場順序、個数、型といった情報を MPIの関数に渡して型として登録します。登録すると型のコードを発行してもらう ことができ、MPIの各関数において、型コード(MPI_Type型)の値として指定する ことが可能になります。
このサンプルでは 2プロセスの間でデータをrank 0 から rank 1 に送信します。
openmpi を用いた実行例を示します。:
$ mpic++ -o mpi_sample11 mpi_sample11.cpp
$ mpiexec -n 2 mpi_sample11
rank: 0, send_data: [
{ boat_id: 0, pos: [ 1.01010096073150635 1.01010096073150635 ], heading: 0, vel: 1.01010101010101017}
{ boat_id: 1, pos: [ 2.0202019214630127 2.0202019214630127 ], heading: 0.785398163397448279, vel: 2.02020202020202033}
{ boat_id: 2, pos: [ 3.03030300140380859 3.03030300140380859 ], heading: 1.57079632679489656, vel: 3.03030303030303028}
]
rank: 1, recv_data: [
{ boat_id: 0, pos: [ 1.01010096073150635 1.01010096073150635 ], heading: 0, vel: 1.01010101010101017}
{ boat_id: 1, pos: [ 2.0202019214630127 2.0202019214630127 ], heading: 0.785398163397448279, vel: 2.02020202020202033}
{ boat_id: 2, pos: [ 3.03030300140380859 3.03030300140380859 ], heading: 1.57079632679489656, vel: 3.03030303030303028}
]