余白の書きなぐり

aueweのブログ

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