MPI_Bcast 动态二维数组

Posted

技术标签:

【中文标题】MPI_Bcast 动态二维数组【英文标题】:MPI_Bcast a dynamic 2d array 【发布时间】:2011-07-03 13:18:48 【问题描述】:

我正在尝试将带有 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();

由于某种原因,我无法理解我得到分段错误。 有谁知道问题出在哪里?

【问题讨论】:

@davidb 和@jackn:伙计们,他想要一个带有指向 10 个数组的指针的数组(这些数组应包含浮点数),如果你告诉他 malloc 100*sizeof(float) 你也应该告诉他如何设置行优先或列优先排序的指针。 【参考方案1】:

这里有三个问题 - 一个涉及分配,一个涉及分配位置,一个涉及 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 中声明了一个非常好的二维数组,但这不是 MPI 所期望的。当您拨打此电话时

MPI_Bcast(array,10*10,MPI_FLOAT,0,MPI_COMM_WORLD);

您告诉 MPI 发送 array 指向的 100 个 连续 浮点数。您注意到库例程无法知道 array 是否是指向 2d 或 3d 或 12d array 开头的指针,或者各个维度是什么;它不知道它是否必须遵循指针,如果这样做了,它也不知道要遵循多少。

所以你想发送一个浮点指针到 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);

请注意,对于任意排列的数据,您仍然可以通过定义一个 MPI 数据类型来执行Bcast,该数据类型描述了二维数组是如何实际在内存中布局的;但这更简单,更接近您可能真正想要的。

(*) 这里真正的问题是 C 和 C 派生的语言没有真正的多维数组作为第一类对象——这对于系统编程语言来说很好,但在进行科学编程时却是不可挽回的恼人。

【讨论】:

或者,可以使用一维缓冲区数组并从 bcasted 缓冲区重新构造 n 维数组【参考方案2】:

array 应该是 100 而不是 10,因为您为每行分配了 10 个浮点数。 JackN 的答案有执行此操作的代码。

但是,在 rank 0 以外的任何进程上,指向数组的指针将是 null。 您需要在所有进程上初始化数组,然后在根上填充数组。

您可以将 malloc 代码移出 if (rank ==0) 块,它应该可以按照您的预期工作。

【讨论】:

【参考方案3】:

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

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

【讨论】:

你的意思是,如果我想要一个 10x10 的二维数组,我必须分配一个 100x1 的数组?那么我可以像二维数组一样访问它吗? 是的。并摆脱for(i=0;i&lt;10;i++) array[i] = (float *)malloc(10*sizeof(float));在尝试访问不允许的内存位置时发生分段错误。【参考方案4】:

您可能想将第一个 malloc 更改为

malloc(10*sizeof(void*)) 

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

array[i][j]=1.0;

【讨论】:

【参考方案5】:

如果你想分配一个10*10的数组,你的代码:

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

应该是

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

【讨论】:

以上是关于MPI_Bcast 动态二维数组的主要内容,如果未能解决你的问题,请参考以下文章

将二维数组复制到函数外部动态分配的二维数组

利用c++中的vector创建动态二维数组

c++ 动态分配二维数组 new 二维数组

C语言如何给用函数二维数组动态赋值

创建动态二维数组

动态分配的二维数组