MPI_Type_create_subarray和MPI_Gather

我必须解决一个小的mpi问题。 我有4个从属进程,每个进程都希望向master 0发送一个2d子arrays(CHUNK_ROWS X CHUNK_COLUMNS).Master 0收集ddd [ROWS] [COLUMNS]中的所有块并打印出来。 我想用MPI_Gather()

#include  #include  using namespace std; #define ROWS 10 #define COLUMNS 10 #define CHUNK_ROWS 5 #define CHUNK_COLUMNS 5 #define TAG 0 int** alloca_matrice(int righe, int colonne) { int** matrice=NULL; int i; matrice = (int **)malloc(righe * sizeof(int*)); if(matrice != NULL){ matrice[0] = (int *)malloc(righe*colonne*sizeof(int)); if(matrice[0]!=NULL) for(i=1; i<righe; i++) matrice[i] = matrice[0]+i*colonne; else{ free(matrice); matrice = NULL; } } else{ matrice = NULL; } return matrice; } int main(int argc, char* argv[]) { int my_id, numprocs,length,i,j; int ndims, sizes[2],subsizes[2],starts[2]; int** DEBUG_CH=NULL; int** ddd=NULL; char name[BUFSIZ]; MPI_Datatype subarray=NULL; //MPI_Status status; MPI_Init(&argc, &argv) ; MPI_Comm_rank(MPI_COMM_WORLD, &my_id) ; MPI_Comm_size(MPI_COMM_WORLD, &numprocs) ; // Ottiene quanti processi sono attivi MPI_Get_processor_name(name, &length); if(my_id!=0){ //creo una sottomatrice ripulita dalle ghost cells ndims=2; sizes[0] = CHUNK_ROWS+2; sizes[1] = CHUNK_COLUMNS+2; subsizes[0] = CHUNK_ROWS; subsizes[1] = CHUNK_COLUMNS; starts[0] = 1; starts[1] = 1; MPI_Type_create_subarray(ndims,sizes,subsizes,starts,MPI_ORDER_C,MPI_INT,&subarray); MPI_Type_commit(&subarray); DEBUG_CH = alloca_matrice(CHUNK_ROWS+2,CHUNK_COLUMNS+2); for(i=0; i<CHUNK_ROWS+2; i++){ for(j=0; j<CHUNK_COLUMNS+2; j++){ if(i==0 || i==CHUNK_ROWS+1 || j==0 || j==CHUNK_COLUMNS+1) DEBUG_CH[i][j] = 5; else DEBUG_CH[i][j] = 1; } } //MPI_Send(DEBUG_CH[0],1,subarray,0,TAG,MPI_COMM_WORLD); } if(my_id==0){ ddd = alloca_matrice(ROWS,COLUMNS); } MPI_Gather(DEBUG_CH[0],1,subarray,ddd[0],CHUNK_ROWS*CHUNK_COLUMNS,MPI_INT,0,MPI_COMM_WORLD); if(!my_id){ for(i=0; i<ROWS; i++){ for(j=0; j<COLUMNS; j++){ printf("%d ",ddd[i][j]); } printf("\n"); } } if(my_id) MPI_Type_free(&subarray); MPI_Finalize(); // Chiusura di MPI. return 0; } 

谢谢大家。

所以这有点微妙,需要了解Gather集体如何放置复杂的类型。

如果你看一下MPI_Gather的大多数例子 ,它们都是一维数组,并且很容易解释应该发生什么; 你从每个进程得到(比方说)10个整数,而Gather足够聪明,可以从开始时的0级中获得10个整数,从数组中的10-19个等级1中获得10个,依此类推。

但是,像这样的更复杂的布局有点复杂。 首先,从发送者的角度来看,数据布局与来自接收者的数据布局不同。 在发送者的角度来看,你从数组元素[1][2] ,转到[1][5] (在大小为7×7的数组中),然后跳转到数组元素[2][3][2][5] [2][3] [2][5]等。有CHUNK_ROWS数据块,每个数据块分隔2个整数。

现在考虑接收者如何接收它们。 假设它正在接收0级数据。 它将接收到数组元素[0][0]-[0][4] – 到目前为止一直很好; 但随后它会在[1][0]-[1][4]中接收下一个数据块,大小为10×10。 这是超过5个元素的跳跃。 内存中的布局是不同的。 因此,接收器必须接收到发送者正在发送的不同的子Subarray类型,因为内存布局不同。

所以,虽然你可能会发送一些看起来像这样的东西:

  sizes[0] = CHUNK_ROWS+2; sizes[1] = CHUNK_COLUMNS+2; subsizes[0] = CHUNK_ROWS; subsizes[1] = CHUNK_COLUMNS; starts[0] = 1; starts[1] = 1; MPI_Type_create_subarray(ndims,sizes,subsizes,starts,MPI_ORDER_C,MPI_INT,&sendsubarray); MPI_Type_commit(&sendsubarray); 

你会收到这样的东西:

  sizes[0] = ROWS; sizes[1] = COLUMNS; subsizes[0] = CHUNK_ROWS; subsizes[1] = CHUNK_COLUMNS; starts[0] = 0; starts[1] = 0; MPI_Type_create_subarray(ndims,sizes,subsizes,starts,MPI_ORDER_C,MPI_INT,&recvsubarray); MPI_Type_commit(&recvsubarray); 

至关重要的是,请注意sizes数组的差异。

现在我们越来越近了。 请注意您的MPI_Gather行更改为以下内容:

 MPI_Gather(DEBUG_CH[0],1,sendsubarray,recvptr,1,recvsubarray,0,MPI_COMM_WORLD); 

有一些事情与以前的版本MPI_Gather(DEBUG_CH[0],1,subarray,ddd[0],CHUNK_ROWS*CHUNK_COLUMNS,MPI_INT,0,MPI_COMM_WORLD);MPI_Gather(DEBUG_CH[0],1,subarray,ddd[0],CHUNK_ROWS*CHUNK_COLUMNS,MPI_INT,0,MPI_COMM_WORLD); – 首先,请注意您正在引用ddd[0] ,但是除了排名0之外的每个排名, ddd=NULL ,因此这将失败。 因此,创建一个名为say recvptr的新变量,在排名为零的情况下,将其设置为ddd[0] 。 (其他进程认为它在哪里并不重要,因为它们没有收到。)另外,我认为你不想接收CHUNK_ROWS*CHUNK_COLUMS MPI_INTs ,因为这会将它们连续地放在内存中,而我的理解是你希望它们以与奴隶任务相同的方式布局,但是在更大的arrays中。

好的,所以现在我们已经到了某个地方,但由于一个有趣的原因,上述情况仍然无法解决。 对于1d数组示例,很容易找出数据排在第n位的位置。 它的计算方法是找到收到的数据的范围 ,然后在那个之后开始下一个元素。 但这不会起作用。 “就在”排名零数据的结尾不是排名第一的数据应该开始的地方( [0][5] )而是[4][5] – 排名0s子arrays中最后一个元素之后的元素。 在这里,您从不同级别回收的数据重叠! 因此,我们将不得不摆弄数据类型的范围,并手动指定每个排名数据的开始位置。 第二个是容易的部分; 当您需要手动指定每个处理器或其中的数据量时,可以使用MPI_Gatherv函数。 第一个是棘手的部分。

MPI让你指定给定数据类型的下限和上限 – 在给定一块内存的情况下,这种类型的第一位数据将会去,以及它“结束”的位置,这里只表示下一个数据的位置可以开始。 (数据可以超出类型的上限,我认为这会使这些名称误导,但这就是事情的方式。)你可以指定它为你喜欢的任何东西,方便你; 因为我们将处理int数组中的元素,所以让我们的类型为MPI_INT的范围。

  MPI_Type_create_resized(recvsubarray, 0, 1*sizeof(int), &resizedrevsubarray); MPI_Type_commit(&resizedrecvsubarray); 

(注意我们只需要为接收类型执行此操作;从发送类型,因为我们只发送其中一个,所以无所谓)。

现在,我们将使用gatherv指定每个元素的起始位置 – 以这个新的resized类型的“size”为单位,它只是1个整数。 因此,如果我们想要在[0][5]处进入大数组,那么从大数组开始的位移是5; 如果我们希望它在位置[5][5]进入,那么位移是55。

最后,请注意聚集和分散集体都假设即使是“主人”也参与其中。 如果即使主人拥有他们自己的全局数组,也最简单。

所以,以下内容适用于我:

 #include  #include  #include  using namespace std; #define ROWS 10 #define COLUMNS 10 #define CHUNK_ROWS 5 #define CHUNK_COLUMNS 5 #define TAG 0 int** alloca_matrice(int righe, int colonne) { int** matrice=NULL; int i; matrice = (int **)malloc(righe * sizeof(int*)); if(matrice != NULL){ matrice[0] = (int *)malloc(righe*colonne*sizeof(int)); if(matrice[0]!=NULL) for(i=1; i