MPI列循环分配2d数组从root到其他进程

时间:2010-09-20 11:46:14

标签: c multithreading mpi

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

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

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

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

对于矩阵使用1-D阵列而不是2-D阵列是否更明智?

修改

经过一番思考后,很明显,如果我使用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) ;

2 个答案:

答案 0 :(得分: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调用中,我不能只使用&amp;(A [0] [0]),因为除了其中一个进程外,它只取消引用空指针。

从一列到几列非常简单;列数据结构从1d数组变为2d数组,如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。

答案 1 :(得分:0)

我不确定我完全明白你要做什么,也不知道怎么做,所以这可能是不合适的:

看起来好像是在尝试在多个进程中分发2D数组的列。也许您有一个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,那么你可能需要用额外的列填充数组,但这似乎有点无意义。

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