【问题标题】:MPI column cyclic distribution of 2d array from root to other processes二维数组从根到其他进程的MPI列循环分布
【发布时间】:2011-04-14 15:33:47
【问题描述】:

我有一个使用 MPI 库的 C 程序。我初始化了一个动态二维数组,其维度(行和列)从进程根的stdin 中读取。

当我尝试在其他进程之间循环分配元素(列)时,我没有取得任何进展。我使用MPI_Scatter 将列分配给数组中的其他进程。在那里,我将派生数据类型MPI_Type_vector 用于二维数组。

当然,它只将第一列分配给进程的本地一维数组。因此,对于其余部分,我将MPI_Scatter 放在一个 for 循环中,现在我已经分配了所有列,但仅适用于进程数和矩阵维度相等的情况。 如何使用MPI_Scatter 将多个列分配给一个进程?

到目前为止,我怀疑这是解决问题的最佳尝试,因为必须有更好的方法,减少沟通。

将一维数组用于矩阵而不是二维数组是否更明智?

编辑:

经过一番思考,很明显如果我使用for循环,派生数据类型MPI_Type_vector就变得不必要了。这表明 for 循环不会让我更进一步。

for(i=0 ;i<m; i++)
    MPI_Scatter(&(array[i][0]), 1,  ub_mpi_t, &local_array[i], 1, MPI_DOUBLE, 0,
                 MPI_COMM_WORLD) ;

【问题讨论】:

    标签: c multithreading mpi


    【解决方案1】:

    好的,让我们先试试这个简单的例子——每个进程只有一列。以下是我对您上面的内容稍作编辑的版本;我想指出的区别只是我们改变了数组 A 的分配方式,我们只使用了一种向量数据类型:

    #include <mpi.h>
    #include <stdlib.h>
    
    int main(int argc, char** argv)
    {
      double **A = NULL ;   /*2D array initialised on process 0 */
      double *Adata = NULL;
      double *sendbufptr = NULL;
      int i,j ;
      double *column ; /*1D array for column */
      const int columnlen=6;
      int my_rank, p ;
      MPI_Datatype vector_mpi_t ;
    
      MPI_Init(&argc,&argv) ;
    
      MPI_Comm_rank(MPI_COMM_WORLD,&my_rank) ;
      MPI_Comm_size(MPI_COMM_WORLD,&p) ;
    
      /*initialise 2D array on process 0 and allocate memory*/
      if(my_rank==0)
      {
        A = (double**)malloc(p*sizeof(double *)) ;
        Adata = (double *)malloc(p*columnlen*sizeof(double));
        for(i=0;i<p;i++)
          A[i] = &(Adata[i*columnlen]);
    
        for (i=0; i<p; i++) 
            for (j=0; j<columnlen; j++)
                A[i][j] = i;
    
        /* print 2D array to screen */
        printf("Rank 0's 2D array:\n");
        for(i=0;i<p;i++)
        {
          for(j=0;j<columnlen;j++)
            printf( "%lf " , A[i][j]) ;
          printf( "\n") ;
        }
        printf( "\n") ;
        printf( "\n") ;
      }
      /* initialise and allocate memory for 1d column array on every process */
      column = (double*)malloc(columnlen*sizeof(double)) ;
      for(i=0;i<columnlen;i++)
      {
        column[i] = 0 ;
      }
    
      /*derived datatype for 2D array columns*/
      MPI_Type_vector(columnlen,1,1,MPI_DOUBLE,&vector_mpi_t) ;
      MPI_Type_commit(&vector_mpi_t);
    
      sendbufptr = NULL;
      if (my_rank == 0) sendbufptr=&(A[0][0]);
      MPI_Scatter(sendbufptr, 1, vector_mpi_t, column, 1, vector_mpi_t, 0, MPI_COMM_WORLD);
      /*print column on every process */
    
       printf("Rank %d's column: \n", my_rank);
       for(i=0;i<columnlen;i++)
       {
          printf( "%lf " , column[i]) ;
       }
       printf( "\n") ;
    
    
      MPI_Finalize() ;
    
      free(column);
      free(Adata);
      free(A);
    
      return 0;
    }
    

    这里的关键是 MPI_Scatter 需要一个指向数据块的指针——而不是指向指针的指针。所以它不会取消引用 A[1] 然后发送指向那里的内容,然后发送 A[2] 和指向那里的内容等。它需要一个连续的数据块。所以我们已经安排了 A 的数据在内存中的布局方式(注意,这通常是数值计算的正确方法)——它有一列数据,后面跟着下一列数据,等等。(虽然我打印数据的方式更像是行,但无论如何。)

    还要注意,在 MPI_Scatter 调用中,我不能只使用 &(A[0][0]),因为这会在除一个进程之外的所有进程中取消引用空指针。

    从一列到多列非常简单;列数据结构从一维数组变为像 A 一样布局的二维数组。

    #include <mpi.h>
    #include <stdlib.h>
    
    int main(int argc, char** argv)
    { 
      double **A = NULL ;   /*2D array initialised on process 0 */
      double *Adata = NULL;
      double *sendbufptr = NULL;
      int i,j ;
      double **columns ; /*2D array for column */
      double *columndata;
      const int columnlen=6;
      int ncolumns;
      int my_rank, p ;
      MPI_Datatype vector_mpi_t ;
    
      MPI_Init(&argc,&argv) ;
    
      MPI_Comm_rank(MPI_COMM_WORLD,&my_rank) ;
      MPI_Comm_size(MPI_COMM_WORLD,&p) ;
    
      ncolumns = 2*p;
    
      /*initialise 2D array on process 0 and allocate memory*/
      if(my_rank==0)
      {
        A = (double**)malloc(ncolumns*sizeof(double *)) ;
        Adata = (double *)malloc(ncolumns*columnlen*sizeof(double));
        for(i=0;i<ncolumns;i++)
          A[i] = &(Adata[i*columnlen]);
    
        for (i=0; i<ncolumns; i++) 
            for (j=0; j<columnlen; j++)
                A[i][j] = i;
    
        /* print 2D array to screen */
        printf("Rank 0's 2D array:\n");
        for(i=0;i<ncolumns;i++)
        {
          for(j=0;j<columnlen;j++)
            printf( "%lf " , A[i][j]) ;
          printf( "\n") ;
        }
        printf( "\n") ;
        printf( "\n") ;
      }
      /* initialise and allocate memory for 1d column array on every process */
      columndata = (double*)malloc((ncolumns/p)*columnlen*sizeof(double)) ;
      columns = (double **)malloc((ncolumns/p)*sizeof(double *));
      for(i=0;i<(ncolumns/p);i++)
      {
        columns[i] = &(columndata[i*columnlen]);
      }
    
      /*derived datatype for 2D array columns*/
      MPI_Type_vector(columnlen,1,1,MPI_DOUBLE,&vector_mpi_t) ;
      MPI_Type_commit(&vector_mpi_t);
    
      sendbufptr = NULL;
      if (my_rank == 0) sendbufptr=&(A[0][0]);
      MPI_Scatter(sendbufptr, (ncolumns/p), vector_mpi_t, &(columns[0][0]), (ncolumns/p), vector_mpi_t, 0, MPI_COMM_WORLD);
    
      /*print columns on every process */
    
       printf("Rank %d's columns: \n", my_rank);
       for(i=0;i<ncolumns/p;i++)
       {
         printf( "[%d]: ", my_rank) ;
         for(j=0;j<columnlen;j++)
         {
            printf( "%lf " , columns[i][j]) ;
         }
         printf( "\n") ;
      }
    
      MPI_Finalize() ;
    
      free(columns);
      free(Adata);
      free(A);
    
      return 0;
    }
    

    然后每个处理器的列数不同需要使用 MPI_Scatterv 而不是 MPI_Scatter。

    【讨论】:

      【解决方案2】:

      我不确定我是否完全理解你想要做什么,也不知道如何做,所以这可能是不正确的:

      看起来好像您正在尝试将二维数组的列分布到多个进程中。也许你有一个 10 列的数组和 4 个进程,所以 2 个进程将得到 3 列,而 2 个进程将得到 2 列?当然,进程 0 从自身“获取”3 列。

      您已经采取的第一步是定义一个 MPI_Type_vector,它定义了数组的一列。

      接下来,在这里我对您使用 MPI_Scatter 感到有些困惑,为什么不简单地编写一个循环,将列 1、2、3、4、5、6、7、8、9、10 发送到进程 0, 1,2,3,0,1,2,3,0,1(哦,之前已经安排接收进程有一个二维数组,以便在列到达时将它们放入)?

      为此使用 MPI_Scatter 的问题是您(我认为)向每个接收进程发送相同数量的数据,正如您所观察到的,如果进程数不完全一致,这将不起作用分成数组中的列数。如果你必须使用 MPI_Scatter 那么你可能需要用额外的列来填充你的数组,但这似乎有点毫无意义。

      最后,您也许可以使用 MPI_Type_create_subarray 或 MPI_Type_create_darray 在 1 个语句中执行您想要的操作,但我没有使用这些的经验。

      【讨论】:

        猜你喜欢
        • 2011-07-28
        • 2021-04-28
        • 1970-01-01
        • 1970-01-01
        • 2016-07-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-09-27
        相关资源
        最近更新 更多