MPI广播2D数组

3
我有一个二维双精度数组,正在被多个进程并行操作。每个进程都操作数组的一部分,在每次迭代结束时,我需要确保所有进程都拥有相同的二维数组副本。
假设数组大小为10*10且有2个进程(或处理器)。进程1(P1)操作2D数组的前5行(总共50个元素),P2操作后5行(总共50个元素)。在每次迭代结束时,我需要P1拥有(自己的前5行+ P2的后5行)。P2应该拥有(P1的前5行+自己的后5行)。希望情景清晰明了。
我正试图使用下面给出的代码进行广播。但我的程序始终以此错误退出:“APPLICATION TERMINATED WITH THE EXIT STRING: Hangup (signal 1)”。
如此问题已经很明确了,我尝试使用Jonathan在这里指出的连续2D内存分配器:MPI_Bcast a dynamic 2d array,但我仍然得到相同的错误。
有人可以帮帮我吗?
我的代码:
double **grid, **oldgrid;
int gridsize; // size of grid
int rank, size; // rank of current process and no. of processes
int rowsforeachprocess, offset; // to keep track of rows that need to be handled by each process

 /* allocation, MPI_Init, and lots of other stuff */

 rowsforeachprocess = ceil((float)gridsize/size);
 offset = rank*rowsforeachprocess;

 /* Each process is handling "rowsforeachprocess" #rows.
 * Lots of work done here
 * Now I need to broadcast these rows to all other processes.
 */

 for(i=0; i<gridsize; i++){
     MPI_Bcast(&(oldgrid[i]), gridsize-2, MPI_DOUBLE, (i/rowsforeachprocess), MPI_COMM_WORLD);
 }

第二部分:上面的代码是使用一维分解的拉普拉斯方程并行求解器的一部分,我不想使用Master-worker模型。如果我使用Master-worker模型,我的代码会更容易吗?

1个回答

2
这里导致崩溃的问题是2D数组指针问题——&(oldgrid[i])是一个指向双精度浮点数的指针的指针,而不是指向双精度浮点数的指针,它指向数组中第i行的指针,而不是第i行。你需要使用MPI_Bcast(&(oldgrid[i][0]),..MPI_Bcast(oldgrid[i],...

还有另一种方法,只使用一个昂贵的集体通信器,而不是每行一个;如果你需要每个人都有整个数组的副本,可以使用MPI_Allgather将数据聚集在一起并分发给每个人;或者,在进程没有相同数量的行的一般情况下,使用MPI_Allgatherv。与广播循环不同,这看起来有点像:

{
    int *counts = malloc(size*sizeof(int));
    int *displs = malloc(size*sizeof(int));
    for (int i=0; i<size; i++) {
        counts[i] = rowsforeachprocess*gridsize;
        displs[i] = i*rowsforeachprocess*gridsize;
    }
    counts[size-1] = (gridsize-(size-1)*rowsforeachprocess)*gridsize;

    MPI_Allgatherv(oldgrid[offset], mynumrows*gridsize, MPI_DOUBLE,
                   oldgrid[0],      counts, displs, MPI_DOUBLE, MPI_COMM_WORLD);
    free(counts);
    free(displs);
}

其中counts是每个任务发送的项目数,displs是偏移量。

但最后,您确定每个进程都必须拥有整个数组的副本吗?如果您只是计算拉普拉斯,那么您可能只需要相邻的行,而不是整个数组。

这将看起来像:

int main(int argc, char**argv) {
double **oldgrid;
const int gridsize=10; // size of grid
int rank, size;        // rank of current process and no. of processes
int rowsforeachprocess; // to keep track of rows that need to be handled by each process
int offset, mynumrows;
MPI_Status status;

MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);

rowsforeachprocess = (int)ceil((float)gridsize/size);
offset = rank*rowsforeachprocess;
mynumrows = rowsforeachprocess;
if (rank == size-1)
    mynumrows = gridsize-offset;

rowsforeachprocess = (int)ceil((float)gridsize/size);
offset = rank*rowsforeachprocess;
mynumrows = rowsforeachprocess;
if (rank == size-1)
    mynumrows = gridsize-offset;

malloc2ddouble(&oldgrid, mynumrows+2, gridsize);

for (int i=0; i<mynumrows+2; i++)
    for (int j=0; j<gridsize; j++)
        oldgrid[i][j] = rank;

/* exchange row data with neighbours */
int highneigh = rank+1;
if (rank == size-1) highneigh = 0;

int lowneigh  = rank-1;
if (rank == 0)  lowneigh = size-1;

/* send data to high neibhour and receive from low */

MPI_Sendrecv(oldgrid[mynumrows], gridsize, MPI_DOUBLE, highneigh, 1,
             oldgrid[0],         gridsize, MPI_DOUBLE, lowneigh,  1,
             MPI_COMM_WORLD, &status);

/* send data to low neibhour and receive from high */

MPI_Sendrecv(oldgrid[1],           gridsize, MPI_DOUBLE, lowneigh,  1,
             oldgrid[mynumrows+1], gridsize, MPI_DOUBLE, highneigh, 1,
             MPI_COMM_WORLD, &status);


for (int proc=0; proc<size; proc++) {
    if (rank == proc) {
        printf("Rank %d:\n", proc);
        for (int i=0; i<mynumrows+2; i++) {
            for (int j=0; j<gridsize; j++) {
                printf("%f ", oldgrid[i][j]);
            }
            printf("\n");
        }
        printf("\n");
    }
    MPI_Barrier(MPI_COMM_WORLD);
}

我刚刚自己找到了MPI_Bcast指针地址的问题。 :) 但是感谢您指出Allgather命令。另外,您所说的每个进程只需要从其相邻进程获取1行数据的方法非常有道理!我会尝试一下。 - Neo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接