余白の書きなぐり

aueweのブログ

OpenMPI で配列をまとめる

やりたいこと

プロセス数 allrank を 4 として、 各 myrank の配列 my_array[3]

myrank == 0 ... my_array[3] = {0,1,2};
myrank == 1 ... my_array[3] = {3,4,5};
myrank == 2 ... my_array[3] = {6,7,8};
myrank == 3 ... my_array[3] = {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}

を作りたい。

戦略

MPI_Gather と MPI_Bcast を組み合わせれば、達成できそう。

  1. MPI_Gather

    MPI_Gather

  2. MPI_Bcast

    MPI_Bcast

ソースコード

// distribute_array.c

// myrank == 0 : my_array[3] = {0,1,2};
// myrank == 1 : my_array[3] = {3,4,5};
// myrank == 2 : my_array[3] = {6,7,8};
// myrank == 3 : my_array[3] = {9,10,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] = 0 + 3*myrank;
  my_array[1] = 1 + 3*myrank;
  my_array[2] = 2 + 3*myrank;
  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に集めて代入
  MPI_Gather( 
    my_array       , // 送信する配列の最初のアドレス
                     // 送信データが配列ではなく、普通の値の場合でも、アドレスを渡す
    3              , // 各ランク毎に何個のデータを送信するのか
                     // 送信データが配列ではなく、普通の値の場合は 1 とする
    MPI_INT        , // 送信するデータの型。'int' ではないので注意
    all_array      , // 受信する配列の最初のアドレス
    3              , // 各ランク毎に何個のデータを受信するのか
                     // 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=3/4  9 10 11
Before Bcast rank=3/4 :0 0 0 0 0 0 0 0 0 0 0 0 
rank=2/4  6 7 8
rank=0/4  0 1 2
Before Bcast rank=2/4 :0 0 0 0 0 0 0 0 0 0 0 0 
rank=1/4  3 4 5
Before Bcast rank=1/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 

参考: