将2D阵列的分布式块发送到MPI的根过程[英] Sending distributed chunks of a 2D array to the root process in MPI

本文是小编为大家收集整理的关于将2D阵列的分布式块发送到MPI的根过程的处理方法,想解了将2D阵列的分布式块发送到MPI的根过程的问题怎么解决?将2D阵列的分布式块发送到MPI的根过程问题的解决办法?那么可以参考本文帮助大家快速定位并解决问题。

问题描述

i有一个2D数组,该数组分布在MPI过程网格上(此示例中的3 x 2个进程).数组的值是在数组的分布的过程中生成的,我想在根过程中将所有这些块聚集在一起以显示它们.

到目前为止,我有以下代码.这会生成笛卡尔通讯器,找到MPI过程的坐标,并根据该过程弄清楚它应该得到多少数组(因为数组不必是笛卡尔网格大小的倍数).然后,我创建了一个新的MPI派生数据类型,该数据类型将将整个过程子阵列发送为一个项目(即每个过程的步幅,区块长度和计数都不同,因为每个过程都有不同的尺寸数组).但是,当我与mpi_gather一起收集数据时,我会得到一个细分错误.

我认为这是因为我不应该使用相同的数据类型在mpi_gather调用中发送和接收.数据类型可以很好地发送数据,因为它具有正确的计数,步幅和区块长度,但是当它到达另一端时,它将需要一个非常不同的派生数据类型.我不确定如何计算此数据类型的参数 - 有人有任何想法吗?

另外,如果我从完全错误的角度接触此操作,请让我知道!

#include<stdio.h>
#include<array_alloc.h>
#include<math.h>
#include<mpi.h>

int main(int argc, char ** argv)
{
    int size, rank;
    int dim_size[2];
    int periods[2];
    int A = 2;
    int B = 3;
    MPI_Comm cart_comm;
    MPI_Datatype block_type;
    int coords[2];

    float **array;
    float **whole_array;

    int n = 10;
    int rows_per_core;
    int cols_per_core;
    int i, j;

    int x_start, x_finish;
    int y_start, y_finish;

    /* Initialise MPI */
    MPI_Init(&argc, &argv);

    /* Get the rank for this process, and the number of processes */
    MPI_Comm_size(MPI_COMM_WORLD, &size);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);

    if (rank == 0)
    {
        /* If we're the master process */
        whole_array = alloc_2d_float(n, n);

        /* Initialise whole array to silly values */
        for (i = 0; i < n; i++)
        {
            for (j = 0; j < n; j++)
            {
                whole_array[i][j] = 9999.99;
            }
        }

        for (j = 0; j < n; j ++)
        {
            for (i = 0; i < n; i++)
            {
                printf("%f ", whole_array[j][i]);
            }
            printf("\n");
        }
    }

    /* Create the cartesian communicator */
    dim_size[0] = B;
    dim_size[1] = A;
    periods[0] = 1;
    periods[1] = 1;

    MPI_Cart_create(MPI_COMM_WORLD, 2, dim_size, periods, 1, &cart_comm);

    /* Get our co-ordinates within that communicator */
    MPI_Cart_coords(cart_comm, rank, 2, coords);

    rows_per_core = ceil(n / (float) A);
    cols_per_core = ceil(n / (float) B);

    if (coords[0] == (B - 1))
    {
        /* We're at the far end of a row */
        cols_per_core = n - (cols_per_core * (B - 1));
    }
    if (coords[1] == (A - 1))
    {
        /* We're at the bottom of a col */
        rows_per_core = n - (rows_per_core * (A - 1));
    }

    printf("X: %d, Y: %d, RpC: %d, CpC: %d\n", coords[0], coords[1], rows_per_core, cols_per_core);

    MPI_Type_vector(rows_per_core, cols_per_core, cols_per_core + 1, MPI_FLOAT, &block_type);
    MPI_Type_commit(&block_type);

    array = alloc_2d_float(rows_per_core, cols_per_core);

    if (array == NULL)
    {
        printf("Problem with array allocation.\nExiting\n");
        return 1;
    }

    for (j = 0; j < rows_per_core; j++)
    {
        for (i = 0; i < cols_per_core; i++)
        {
            array[j][i] = (float) (i + 1);
        }
    }

    MPI_Barrier(MPI_COMM_WORLD);

    MPI_Gather(array, 1, block_type, whole_array, 1, block_type, 0, MPI_COMM_WORLD);

    /*
    if (rank == 0)
    {
        for (j = 0; j < n; j ++)
        {
            for (i = 0; i < n; i++)
            {
                printf("%f ", whole_array[j][i]);
            }
            printf("\n");
        }
    }
    */
    /* Close down the MPI environment */
    MPI_Finalize();
}

我上面使用的2D数组分配例程被实现为:

float **alloc_2d_float( int ndim1, int ndim2 ) {

  float **array2 = malloc( ndim1 * sizeof( float * ) );

  int i;

  if( array2 != NULL ){

    array2[0] = malloc( ndim1 * ndim2 * sizeof( float ) );

    if( array2[ 0 ] != NULL ) {

      for( i = 1; i < ndim1; i++ )
    array2[i] = array2[0] + i * ndim2;

    }

    else {
      free( array2 );
      array2 = NULL;
    }

  }

  return array2;

}

推荐答案

看起来您的第一个参数MPI_Gather呼叫可能是array[0],而不是array.

.

另外,如果您需要从每个等级获得不同数量的数据,则最好使用MPI_Gatherv.

.

最后,在许多情况下,在一次进行输出的地方收集所有数据并不是可扩展的.随着数据的增加,最终它将超过排名0的内存.您可能会更好地分发输出工作(如果您写入文件,使用MPI io或其他库呼叫)或执行点 - 待办事项一次发送至等级0,以限制总内存消耗.

另一方面,我会 建议协调您的每个等级打印到标准输出,因为某些主要的MPI实现不保证将按顺序产生标准输出.尤其是Cray的MPI,如果多个等级打印,则会彻底弄乱标准输出.

其他推荐答案

这是一个棘手的.您在正确的轨道上,是的,您将需要不同类型的发送和接收.

发送零件很容易 - 如果您发送了整个子阵列array,那么您甚至不需要矢量类型;您可以从&(array[0][0])开始发送整个(rows_per_core)*(cols_per_core)连续的浮子(如果愿意,或者array[0]).

您收集的正是接收是棘手的部分.让我们从最简单的情况开始 - 假设一切都均匀分裂,以便所有块的大小相同.然后,您可以使用非常helfpul

小事 - 在聚集之前,您不需要障碍.实际上,您几乎真的不需要障碍,而且由于某些原因而它们的昂贵操作,并且可以隐藏问题 - 我的经验法则是永远不要使用障碍,除非您确切地知道为什么规则需要成为在这种情况下破裂.在这种情况下,尤其是集体gather例程与屏障完全相同,因此只需使用它.

现在,进入更艰难的东西.如果事情不均匀划分,您有一些选择.最简单的,尽管不一定是最好的,它只是填充数组,即使仅用于此操作,它即使它均匀地分开.

如果您可以安排它,以使列的数量确实均匀分开,即使行的数量没有,您仍然可以使用gatherv并为行的每个部分创建矢量类型,然后gathingv每个处理器的适当行数.那会很好.

如果您肯定没有可以依靠分隔的情况,并且无法填充数据,那么我可以看到三个子选项:

其他推荐答案

符合由我):

集体操作的类型匹配条件比点对点的发件人和接收器之间的相应条件更严格.即,对于集体操作,发送的数据量必须与接收人指定的数据量完全匹配.仍然允许发件人和接收器之间的不同类型地图.

对我来说,您有两个选择:

  1. PAD较小的子膜片,使所有过程都会发送相同数量的数据,然后在收集后将矩阵裁剪回其原始大小.如果您感到喜欢冒险,则可以尝试定义接收打字,以便在收集操作过程中自动覆盖桨叶,从而消除后来对作物的需求.但是,这可能会变得有些复杂.
  2. 回到点对点通信.更直接但可能更高的沟通成本.

就个人而言,我会选择选项2.

本文地址:https://www.itbaoku.cn/post/359425.html