MPI_Bcast一个动态二维数组

10

我想要通过bcast将一个动态的二维数组传递给所有进程。我有以下代码。

#include <stdlib.h>
#include <mpi.h>

int main(int argc, char **argv)
{   
    float **array;
    int rank,size,i,j;

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

    if(rank==0)
    {
        array = (float **)malloc(10*sizeof(float));
        for(i=0;i<10;i++)
            array[i] = (float *)malloc(10*sizeof(float));

        for(i=0;i<10;i++)
        for(j=0;j<10;j++)
            array[i][j]=i+j;
    }
    MPI_Bcast(array,10*10,MPI_FLOAT,0,MPI_COMM_WORLD);
    MPI_Finalize();
}

由于某些我无法理解的原因,我遇到了分段错误。有人知道问题在哪里吗?


1
@davidb 和 @jackn:伙计们,他想要一个包含指向10个数组的指针的数组(这些数组应该包含浮点数),如果你告诉他要 malloc 100*sizeof(float),你也应该告诉他如何设置行优先或列优先的指针。 - Bernd Elkemann
5个回答

29

这里涉及到三个问题——一个涉及分配,一个涉及分配的位置,一个涉及MPI工作方式,其他答案都没有完全触及它们。

第一个也是最严重的问题是分配的位置。正如@davidb正确指出的那样,当前情况下只在任务零上分配了内存,因此其他任务没有内存可以接收广播。

至于C语言中的二维分配,你的代码几乎完全正确。在这段代码块中:

     array = (float **)malloc(10*sizeof(float));
     for(i=0;i<10;i++)
         array[i] = (float *)malloc(10*sizeof(float));

唯一的真正问题就是第一个 malloc 应该分配 10 个浮点型指针,而不是浮点数:

     array = (float **)malloc(10*sizeof(float *));
     for(i=0;i<10;i++)
         array[i] = (float *)malloc(10*sizeof(float));
这是由@eznme指出的。第一种方法实际上可能会根据你正在编译/链接的内存模型以及几乎肯定在32位操作系统/机器上工作 - 但仅仅因为它可以工作并不意味着它是正确的 :)现在,最后的问题是,您已经在C中声明了一个完全良好的2D数组,但这不是MPI所期望的。当您进行这个调用时:
MPI_Bcast(array,10*10,MPI_FLOAT,0,MPI_COMM_WORLD);

您要告诉MPI发送由array指向的100个连续浮点数。您可能会注意到,库例程无法知道array是2d、3d还是12d数组的起始指针,也不知道每个维度的大小;它不知道是否要跟随指针,如果要跟随指针,也不知道要跟随多少个。

因此,您想要发送一个指向100个连续浮点数的指针,但在C语言中分配伪多维数组的普通方式(*)中,并不一定满足这个条件。您可能不确定第二行在这种布局中与第一行相距多远,甚至不确定方向。因此,您真正想要做的是像这样:

int malloc2dfloat(float ***array, int n, int m) {

    /* allocate the n*m contiguous items */
    float *p = (float *)malloc(n*m*sizeof(float));
    if (!p) return -1;

    /* allocate the row pointers into the memory */
    (*array) = (float **)malloc(n*sizeof(float*));
    if (!(*array)) {
       free(p);
       return -1;
    }

    /* set up the pointers into the contiguous memory */
    for (int i=0; i<n; i++) 
       (*array)[i] = &(p[i*m]);

    return 0;
}

int free2dfloat(float ***array) {
    /* free the memory - the first element of the array is at the start */
    free(&((*array)[0][0]));

    /* free the pointers into the memory */
    free(*array);

    return 0;
}

只有这种方式,你才能确保内存是连续的。然后你可以执行

float **array;
/* ... */
malloc2dfloat(&array, 10, 10);
if (rank == 0) {
    for(i=0;i<10;i++)
         for(j=0;j<10;j++)
              array[i][j]=i+j;
}
MPI_Bcast(&(array[0][0]), 10*10, MPI_FLOAT, 0, MPI_COMM_WORLD);

请注意,对于任意数据排列,您仍可以通过定义描述2d数组在内存中实际布局的MPI数据类型来执行Bcast。但这种方法更简单,更接近您实际想要的。

(*) 这里的真正问题是,C和C派生语言没有真正的多维数组作为一流对象 - 对于系统编程语言来说这很好,但在科学编程时会非常烦人。


1
或者,您可以使用一个一维缓冲数组,并从广播的缓冲区重新构建n维数组。 - OD IUM

7
array 应该是100而不是10,因为你每行分配了10个浮点数。JackN的答案中有代码可以做到这一点。
然而,在除了 rank 0 之外的任何进程上,指向 array 的指针将是 null。 你需要在所有进程上初始化数组,然后在根进程上填充数组。
你只需将 malloc 代码移出 if (rank ==0) 块即可按预期工作。

2

数组应该是100,而不是10。

array = (float **)malloc(100*sizeof(float)); 

你的意思是如果我想要一个10x10的二维数组,我必须分配一个100x1的数组吗?然后我可以像访问二维数组一样访问它吗? - faulpin
是的。并且摆脱 for(i=0;i<10;i++) array[i] = (float *)malloc(10*sizeof(float)); 当尝试访问不允许的内存位置时,会发生分段错误。 - jacknad

1

你可能想要将第一个malloc更改为

malloc(10*sizeof(void*)) 

因为该数组存储指针并存储浮点数而不是整数:

array[i][j]=1.0;

0
如果你想分配一个10*10的数组,你的代码应该是:
array = (float **)malloc(10*sizeof(float))

应该是

array = (float **)malloc(10*sizeof(float*))

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