好的,让我们先试试这个简单的例子——每个进程只有一列。以下是我对您上面的内容稍作编辑的版本;我想指出的区别只是我们改变了数组 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。