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
【方法】
#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]=010+0=0、send[1]=010+1=1、…、send[9] =010+9=9
at プロセス1 send[0]=110+0=10、send[1]=110+1=11、…、send[9] =110+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にさえあれば良いので、以下参考にどうぞ。
#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;
}