MPI_Type_create_subarray 和 MPI_Gather

2024-04-14

我必须解决一些 mpi 问题。我有 4 个从进程,每个进程都想发送一个 2d 子数组 (CHUNK_ROWS X CHUNK_COLUMNS) 到 master 0。Master 0 收集 ddd[ROWS][COLUMNS] 中的所有块并打印它。我想使用 MPI_Gather()

#include <mpi.h>
#include <iostream>
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 的示例 http://www.netlib.org/utk/papers/mpi-book/node99.html,它们是一维数组,并且很容易解释应该发生什么;例如,您从每个进程获得 10 个整数,并且 Gather 足够聪明,可以将排名 0 的 10 个整数放在数组的开头,将排名 1 的 10 个整数放在数组的位置 10-19 处,依此类推。

不过,像这样的更复杂的布局要复杂一些。首先,从发送方的角度来看的数据布局与从接收方的角度来看的数据布局不同。从发送者的角度来看,您从数组元素开始[1][2], go to [1][5](在大小为 7x7 的数组中),然后跳转到数组元素[2][3]-[2][5], ETC。 有 CHUNK_ROWS 数据块,每个数据块由 2 个整数分隔。

现在考虑接收者如何接收它们。假设它正在接收 0 级的数据。它将把它接收到数组元素中[0][0]-[0][4]- 到目前为止,一切都很好;但随后它将接收下一个数据块[1][0]-[1][4],位于大小为 10x10 的数组中。这相当于跳过了 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 array.

现在我们已经更接近了。请注意您的 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);-- 首先,请注意您正在引用ddd[0],但对于除 0 级之外的每个等级,ddd=NULL,所以这将会失败。因此创建一个名为 say 的新变量recvptr,并且在等级零中,将其设置为ddd[0]。 (其他进程认为它在哪里并不重要,因为它们没有接收到。)另外,我认为你不想接收CHUNK_ROWS*CHUNK_COLUMS MPI_INTs,因为这会将它们连续地放置在内存中,我的理解是您希望它们以与工作任务相同的方式布置,但在更大的数组中。

好的,现在我们已经取得进展了,但是上面的内容still由于一个有趣的原因,不起作用。对于一维数组示例,很容易找出第 n 列数据的去向。其计算方法是通过查找extent正在接收的数据,并在该数据之后开始下一个元素。但这在这里行不通。 “就在”零级数据结束之后不是第一级数据应该开始的位置([0][5]) 但反而,[4][5]-- 0 级子数组中最后一个元素之后的元素。在这里,您从不同等级接收到的数据是重叠的!因此,我们将不得不摆弄数据类型的范围,并手动指定每个排名的数据开始的位置。第二个是简单的部分;你使用MPI_Gatherv http://www.mcs.anl.gov/research/projects/mpi/www/www3/MPI_Gatherv.html当您需要手动指定每个处理器的数据量或数据的去向时,可以使用此函数。首先是比较棘手的部分。

MPI 让您指定给定数据类型的下限和上限 - 给定一块内存,该类型的第一位数据将存放在哪里,以及它“结束”的位置,这里仅意味着下一个数据的位置可以开始了。 (数据可以超出类型的上限,我认为这会使这些名称产生误导,但这就是事情的方式。)您可以将其指定为您喜欢的任何内容,这对您来说很方便;因为我们将处理 a 中的元素int数组,让我们将类型的范围设置为 1 MPI_INT 大小。

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

(请注意,我们只需对接收类型执行此操作;对于发送类型,因为我们只发送其中之一,所以这并不重要)。

现在,我们将使用 Gatherv 来指定每个元素的开始位置——以这个新调整大小的类型的“大小”为单位,它只是 1 个整数。所以如果我们想要一些东西进入大数组[0][5],距大数组起始位置的位移为5;如果我们想让它进入位置[5][5],位移为55。

最后,请注意,聚集和分散集体都假设即使是“主”或协调程序进程也在参与。如果协调器也有自己的全局数组部分,那么最容易实现此功能。

因此,以下内容对我有用:

#include <mpi.h>
#include <iostream>
#include <cstdlib>
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;
    int *recvptr=NULL;
    char name[BUFSIZ];
    MPI_Datatype sendsubarray;
    MPI_Datatype recvsubarray;
    MPI_Datatype resizedrecvsubarray;
    //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
    if (numprocs != 4) {
        MPI_Abort(MPI_COMM_WORLD,1);
    }
    MPI_Get_processor_name(name, &length);    

    //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,&sendsubarray);
    MPI_Type_commit(&sendsubarray);

    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] = my_id;
        }
    }

    recvptr=DEBUG_CH[0];
    if(my_id==0){
        ddd = alloca_matrice(ROWS,COLUMNS);
        sizes[0]    = ROWS; sizes[1] = COLUMNS;
        subsizes[0] = CHUNK_ROWS; subsizes[1] = CHUNK_COLUMNS;
        starts[0]   = 0; starts[1] = 0;
        MPI_Type_create_subarray(2,sizes,subsizes,starts,MPI_ORDER_C,MPI_INT,&recvsubarray);
        MPI_Type_commit(&recvsubarray);
        MPI_Type_create_resized(recvsubarray, 0, 1*sizeof(int), &resizedrecvsubarray);
        MPI_Type_commit(&resizedrecvsubarray);
        recvptr = ddd[0];
    }

    int counts[5]={1,1,1,1};
    int disps[5] ={0,5,50,55};
    MPI_Gatherv(DEBUG_CH[0],1,sendsubarray,recvptr,counts,disps,resizedrecvsubarray,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 == 0) {
        MPI_Type_free(&resizedrecvsubarray);
        MPI_Type_free(&recvsubarray);
        free(ddd[0]);
        free(ddd);
    } else {
        MPI_Type_free(&sendsubarray);
        free(DEBUG_CH[0]);
        free(DEBUG_CH);
    }

    MPI_Finalize();                             // Chiusura di MPI.
    return 0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MPI_Type_create_subarray 和 MPI_Gather 的相关文章

随机推荐