OpenMPI で配列をまとめる(2)
前回 とほとんど同じだけど、備忘録として。
やりたいこと
プロセス数 allrank を 4 として、 各 myrank の配列 my_array[3]
myrank == 0 ... my_array[3] = {0,4,8}; // 前回は{0,1,2}だった myrank == 1 ... my_array[3] = {1,5,9}; // 前回は{3,4,5}だった myrank == 2 ... my_array[3] = {2,6,10}; // 前回は{6,7,8}だった myrank == 3 ... my_array[3] = {3,7,11}; // 前回は{9,10,11}だった
をまとめて、各 myrank の配列 all_array[12]
myrank == 0 ... all_array[12] = {0,1,2,3,4,5,6,7,8,9,10,11} myrank == 1 ... all_array[12] = {0,1,2,3,4,5,6,7,8,9,10,11} myrank == 2 ... all_array[12] = {0,1,2,3,4,5,6,7,8,9,10,11} myrank == 3 ... all_array[12] = {0,1,2,3,4,5,6,7,8,9,10,11}
を作りたい。
戦略
前回 のプログラムを発展させよう。
初期配列を
my_array[0] = myrank + 0*allrank; my_array[1] = myrank + 1*allrank; my_array[2] = myrank + 2*allrank;
とするのは良いとして、 MPI_Gather の部分を以下のように改造すればいけそう。
int l; for (l = 0; l < 3; l++) { MPI_Gather( &(my_array[l]) , // 送信する配列の最初のアドレス // 送信データが配列ではなく、普通の値の場合でも、アドレスを渡す 1 , // 各ランク毎に何個のデータを送信するのか // 送信データが配列ではなく、普通の値の場合は 1 とする MPI_INT , // 送信するデータの型。'int' ではないので注意 &(all_array[allrank*l]), // 受信する配列の最初のアドレス 1 , // 各ランク毎に何個のデータを受信するのか // 3つ上の引数と一致するはず MPI_INT , // 受信するデータの型。'int' ではないので注意 0 , // どのrankのall_arrayに入れるか MPI_COMM_WORLD // コミュニケータ。MPI_COMM_WORLD としておく ); }
ソースコード
// distribute_array.c // myrank == 0 : my_array[3] = {0,4,8}; // myrank == 1 : my_array[3] = {1,5,9}; // myrank == 2 : my_array[3] = {2,6,10}; // myrank == 3 : my_array[3] = {3,7,11}; // をまとめて // all_array[12] = {0,1,2,3,4,5,6,7,8,9,10,11} // をつくる #include <stdio.h> #include <mpi.h> int main(int argc, char **argv){ // MPIの初期設定 MPI_Init(&argc, &argv); int myrank, allrank; MPI_Comm_rank(MPI_COMM_WORLD, &myrank); MPI_Comm_size(MPI_COMM_WORLD, &allrank); // 各myrankで配列my_arrayを宣言 int my_array[3]; // 配列初期化 my_array[0] = myrank + 0*allrank; my_array[1] = myrank + 1*allrank; my_array[2] = myrank + 2*allrank; printf("rank=%d/%d %d %d %d\n", myrank, allrank, my_array[0], my_array[1], my_array[2]); // 各myrankで配列all_arrayを宣言 int all_array[12] = {0}; // 3*allrank個の配列 // 各myrankのmy_arrayを、myrank==0のall_arrayに集めて代入 int l; for (l = 0; l < 3; l++) { MPI_Gather( &(my_array[l]) , // 送信する配列の最初のアドレス // 送信データが配列ではなく、普通の値の場合でも、アドレスを渡す 1 , // 各ランク毎に何個のデータを送信するのか // 送信データが配列ではなく、普通の値の場合は 1 とする MPI_INT , // 送信するデータの型。'int' ではないので注意 &(all_array[allrank*l]), // 受信する配列の最初のアドレス 1 , // 各ランク毎に何個のデータを受信するのか // 3つ上の引数と一致するはず MPI_INT , // 受信するデータの型。'int' ではないので注意 0 , // どのrankのall_arrayに入れるか MPI_COMM_WORLD // コミュニケータ。MPI_COMM_WORLD としておく ); } // ここで if(myrank == 0){...} として、作成したall_array[12]を処理すると便利 // この時点では、myrank==0のall_arrayにしかデータが入っていない。 printf("Before Bcast rank=%d/%d :", myrank, allrank); int n; for (n = 0; n < 12; n++) { printf("%d ", all_array[n]); } printf("\n"); // myrank==0のall_arrayを各myrankのall_arrayにコピー MPI_Bcast( all_array , // 送受信する配列の最初のアドレス 12 , // 送受信するデータの数 MPI_INT , // 送受信するデータの型 0 , // どのmyrankのデータを送信するか MPI_COMM_WORLD // コミュニケータ。MPI_COMM_WORLD としておく ); // MPI_Bcastしたので、全てのmyrankのall_arrayが同等になった printf("After Bcast rank=%d/%d :", myrank, allrank); for (n = 0; n < 12; n++) { printf("%d ", all_array[n]); } printf("\n"); MPI_Finalize(); }
コンパイルと実行
$ mpicc ./distribute_array.c $ mpirun -np 4 a.out rank=0/4 0 4 8 rank=1/4 1 5 9 Before Bcast rank=1/4 :0 0 0 0 0 0 0 0 0 0 0 0 rank=2/4 2 6 10 Before Bcast rank=2/4 :0 0 0 0 0 0 0 0 0 0 0 0 rank=3/4 3 7 11 Before Bcast rank=3/4 :0 0 0 0 0 0 0 0 0 0 0 0 Before Bcast rank=0/4 :0 1 2 3 4 5 6 7 8 9 10 11 After Bcast rank=2/4 :0 1 2 3 4 5 6 7 8 9 10 11 After Bcast rank=0/4 :0 1 2 3 4 5 6 7 8 9 10 11 After Bcast rank=1/4 :0 1 2 3 4 5 6 7 8 9 10 11 After Bcast rank=3/4 :0 1 2 3 4 5 6 7 8 9 10 11