LoginSignup
0
0

More than 3 years have passed since last update.

C言語+MPIのtips(MPI_Gatherv)

Last updated at Posted at 2020-12-19

MPI_Gatherv は「各プロセスの持つ必ずしも同じ長さでないデータを結合して,あるプロセスに渡す関数」。

※MPI_Gatherv は「各プロセスの持つ同じ長さのデータを結合して,あるプロセスに渡す関数」だったことに注意。

【目的】
各プロセスに存在する一次元配列 send から適当な数(eachsz)の要素だけ取り出して、プロセス0にある一次元配列 recv に結合させることでMPI_Gatherv の機能を知る。

【使用するもの】

open-mpi

【環境】

$ mpicc —version

Apple clang version 11.0.3 (clang-1103.0.32.29) 

Target: x86_64-apple-darwin19.6.0 

Thread model: posix

InstalledDir: /Library/Developer/CommandLineTools/usr/bin

【方法】

Gatherv.c
#include<stdio.h>
#include<mpi.h>

int main(int argc, char **argv){
        int i;
        int my_rank, num_proc;
        int send[10];
        int recv[20];                // The number of processes is 2
        int eachsz[2] = {3,8};       // The number of processes is 2
        int place[2];                // The number of processes is 2

        MPI_Init(&argc, &argv);
        MPI_Comm_size(MPI_COMM_WORLD, &num_proc);
        MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

        for(i=0;i<10;i++) send[i] = my_rank*10 + i;

        for(i=0;i<10;i++) printf("my_rank:%d, send[%d] = %d\n",my_rank,i,send[i]);

        for(i=0;i<20;i++) recv[i] = -1;
        for(i=0;i<2;i++) place[i] = 10*i;

        MPI_Gatherv(send, eachsz[my_rank], MPI_INT, recv, eachsz, place, MPI_INT, 0,MPI_COMM_WORLD);

        printf("\n");
        printf("my_rank:%d\n",my_rank);
        for(i=0;i<20;i++) printf("%2d ",recv[i]);
        printf("\n");

MPI_Finalize();
return 0;

【結果】
mpicc コマンドでコンパイル、mpirun コマンドでプロセス数を2にして実行。

$ mpicc Gatherv.c
$ mpirun -np 2 ./a.out
my_rank:0, send[0] = 0
my_rank:0, send[1] = 1
my_rank:0, send[2] = 2
my_rank:0, send[3] = 3
my_rank:0, send[4] = 4
my_rank:0, send[5] = 5
my_rank:0, send[6] = 6
my_rank:0, send[7] = 7
my_rank:0, send[8] = 8
my_rank:0, send[9] = 9
my_rank:1, send[0] = 10
my_rank:1, send[1] = 11
my_rank:1, send[2] = 12
my_rank:1, send[3] = 13
my_rank:1, send[4] = 14
my_rank:1, send[5] = 15
my_rank:1, send[6] = 16
my_rank:1, send[7] = 17
my_rank:1, send[8] = 18
my_rank:1, send[9] = 19

my_rank:1
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 

my_rank:0
 0  1  2 -1 -1 -1 -1 -1 -1 -1 10 11 12 13 14 15 16 17 -1 -1 

【考察(何が起きたのか)】
まずプロセス0と1でそれぞれ send[0]、send[1]、…、send[9] が生成される。
プロセス0と1でそれぞれ recv[0]、recv[1]、…、recv[19] が生成される。
プロセス0と1でそれぞれ eachsz[0](=3)、eachsz[1](=8) が生成される。
プロセス0と1でそれぞれ place[0]、place[1] が生成される。

それぞれのプロセスでのfor文で値が格納される。

at プロセス0 send[0]=0*10+0=0、send[1]=0*10+1=1、…、send[9] =0*10+9=9

at プロセス1 send[0]=1*10+0=10、send[1]=1*10+1=11、…、send[9] =1*10+9=19

at プロセス0 recv[0]=-1、 recv[1]=-1、…、recv[19]=-1
at プロセス1 recv[0]=-1、 recv[1]=-1、…、recv[19]=-1

at プロセス0 place[0]=0、 place[1]=10
at プロセス1 place[0]=0、 place[1]=10

MPI_Gathervによって各プロセスの「send」から「eachsz[my_rank]」個の要素を送る。つまり、プロセス0からは3個(eachsz[0]=3)、プロセス1からは8個(eachsz[1]=8)の要素が送られる。送られるデータの型は整数(int)「MPI_INT」。一つのプロセスあたりの送信するデータの数は「eachsz」、各プロセスから受け取るデータの場所(配列のインデックス)は「place」。つまり、プロセス0からの3個のデータは place[0] = 0 よりrecv[0]、recv[1]、recv[2]に代入される。よって
recv[0](= プロセス0の send[0])=0、
recv[1](= プロセス0の send[1])=1、
recv[2](= プロセス0の send[2])=2
となる。さらにプロセス1からの8個のデータは place[1] = 10 よりrecv[10]、recv[11]、…、recv[17]に代入される。よって
recv[10](= プロセス1の send[0])=10、
recv[11](= プロセス1の send[1])=11、
…、
recv[17](= プロセス1の send[7])=17
となる。recv[3]からrecv[9]、また recv[18]からrecv[19]は -1 のまま。
受け取るデータの型は整数(int)「MPI_INT」。結果はプロセス「0」へ送信される。プロセス1の recv の要素は-1のまま。

それぞれのプロセスで recv が表示されて終了。

【参考】

「mpi 並列プログラミング」と検索すると、より正確でより情報量の多い資料が見つかります。

【補足】
実際は recv と place はプロセス0にさえあれば良いので、以下参考にどうぞ。

Gatherv_2.c
#include<stdio.h>
#include<mpi.h>
#include<stdlib.h>          // For malloc
#include<time.h>            // For generating random numbers

#define N 10

int main(int argc, char **argv){
        int i;
        int my_rank, num_proc;
        int send[N];
        int *recv;
        int *eachsz;
        int *place;
        srand(time(NULL));          // For generating random numbers

        MPI_Init(&argc, &argv);
        MPI_Comm_size(MPI_COMM_WORLD, &num_proc);
        MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

        for(i=0;i<N;i++) send[i] = my_rank*N + i;
        for(i=0;i<N;i++) printf("my_rank:%d, send[%d] = %d\n",my_rank,i,send[i]);

        if (my_rank==0){
           recv=(int *)malloc(num_proc*N*sizeof(int));
           if (recv==NULL){
                printf("recv\n");
                return 1;
           }
           for(i=0;i<N*num_proc;i++) recv[i]=-1;

           place=(int *)malloc(num_proc*sizeof(int));
           if (place==NULL){
                printf("place\n");
                return 1;
           }
           for(i=0;i<num_proc;i++) place[i] = N*i;

        }

        eachsz=(int *)malloc(num_proc*sizeof(int));
        if (eachsz==NULL){
                printf("eachsz\n");
                return 1;
        }
        for(i=0;i<num_proc;i++)  eachsz[i]=rand()%N;

        if (my_rank==0){
           for(i=0;i<num_proc;i++)  printf("eachsz[%d] = %d\n",i, eachsz[i]);
        }

        MPI_Gatherv(send, eachsz[my_rank], MPI_INT, recv, eachsz, place, MPI_INT, 0,MPI_COMM_WORLD);

        if (my_rank==0){
           printf("\n");
           printf("my_rank:%d\n",my_rank);
           for(i=0;i<20;i++) printf("%2d ",recv[i]);
           printf("\n");
        }

        if (my_rank==0){
           free(recv); free(place);
        }

       free(eachsz);

MPI_Finalize();
return 0;
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0