【问题标题】:Using MPI_Gather for 3d array in c++?在 C++ 中将 MPI_Gather 用于 3d 数组?
【发布时间】:2011-04-09 14:51:07
【问题描述】:

我正在尝试并行化一个夹在两个 for 循环之间的 for 循环操作。

在每个处理器中计算数据(3d 数组)后,我想将每个处理器的数据收集回根节点以进行进一步处理。我尝试使用MPI_Gather 函数将数据返回到根节点。使用此功能,从根处理器收集回数据,但不从其他处理器收集数据。

int main(int argc, char * argv[]) {

  int i,k,l,j;
  int Np = 7, Nz = 7, Nr = 4;
  int mynode, totalnodes;
  MPI_Status status;
  long double ***k_p, ***k_p1; 
  int startvalp,endvalp;

  MPI_Init(&argc,&argv);
  MPI_Comm_size(MPI_COMM_WORLD, &totalnodes);
  MPI_Comm_rank(MPI_COMM_WORLD, &mynode);

  // Allocation of memory
  Allocate_3D_R(k_p,(Nz+1),(Np+1),(Nr+1));
  Allocate_3D_R(k_p1,(Nz+1),(Np+1),(Nr+1));

  // startvalp represents the local starting value for each processor
  // endvalp represents the local ending value for each processor

  startvalp = (Np+1)*mynode/totalnodes - 0;
  endvalp = startvalp + (((Np+1)/totalnodes) -1);

  for(l = 0 ; l <= 1 ; l++){
    for(k=startvalp; k<=endvalp; k++){
      // for loop parallelized between the processors 
      // original loop: for(k=0; k<= Np; k++) 
      for(i=0; i<=1; i++){
        k_p[i][k][l] =  l+k+i;
      }
    }
  }

  // For Np = 7  and for two processors ; 
  //   k = 0 - 3 is calculated in processor 0;
  //   k = 4 - 7 is calculated in processor 1;

  // Now I need to collect the value of k_p from processor 1 
  // back to the root processor.
  // MPI_Gather function  is used.

  for(l = 0 ; l <= 1 ; l++){
    for(k=startvalp; k<=endvalp; k++){      
      for(i=0; i<=1; i++){
        MPI_Gather(&(k_p[i][k][l]),1, MPI_LONG_DOUBLE,&(k_p1[i][k][l]),1, MPI_LONG_DOUBLE, 0, MPI_COMM_WORLD);
      }
    }
  }

  // Using this the k_p is collected from root processor and stored
  // in the k_p1 variable, but from the slave processor it is not
  // collected back to the root processor.

  if(mynode == 0){
    for(l = 0 ; l <= 1 ; l++){
      for(k=0; k<=Np; k++){
        for(i=0i<=1;i++){
          cout << "Processor "<<mynode;
          cout << ": k_p["<<i<<"]["<<k<<"]["<<l<<"] = " <<k_p1[i][k][l]<<endl;
        }
      }
    }
  }

  MPI_Finalize();

} // end of main


void Allocate_3D_R(long double***& m, int d1, int d2, int d3) {

  m=new long double** [d1];
  for (int i=0; i<d1; ++i) {
    m[i]=new long double* [d2];
    for (int j=0; j<d2; ++j) {
      m[i][j]=new long double [d3];
      for (int k=0; k<d3; ++k) {
        m[i][j][k]=0.0;
      }
    }
  }
}

这是输出:

Processor 0: k_p[0][0][0] = 0
Processor 0: k_p[1][0][0] = 1
Processor 0: k_p[0][1][0] = 1
Processor 0: k_p[1][1][0] = 2
Processor 0: k_p[0][2][0] = 2
Processor 0: k_p[1][2][0] = 3
Processor 0: k_p[0][3][0] = 3
Processor 0: k_p[1][3][0] = 4
Processor 0: k_p[0][4][0] = 0
Processor 0: k_p[1][4][0] = 0
Processor 0: k_p[0][5][0] = 0
Processor 0: k_p[1][5][0] = 0
Processor 0: k_p[0][6][0] = 0
Processor 0: k_p[1][6][0] = 0
Processor 0: k_p[0][7][0] = 0
Processor 0: k_p[1][7][0] = 0
Processor 0: k_p[0][0][1] = 1
Processor 0: k_p[1][0][1] = 2
Processor 0: k_p[0][1][1] = 2
Processor 0: k_p[1][1][1] = 3
Processor 0: k_p[0][2][1] = 3
Processor 0: k_p[1][2][1] = 4
Processor 0: k_p[0][3][1] = 4
Processor 0: k_p[1][3][1] = 5
Processor 0: k_p[0][4][1] = 0
Processor 0: k_p[1][4][1] = 0
Processor 0: k_p[0][5][1] = 0
Processor 0: k_p[1][5][1] = 0
Processor 0: k_p[0][6][1] = 0
Processor 0: k_p[1][6][1] = 0
Processor 0: k_p[0][7][1] = 0
Processor 0: k_p[1][7][1] = 0

从根处理器传输数据,但不从其他处理器传输。 我尝试使用 MPI_SendMPI_Recv 函数并没有遇到上述问题,但是对于较大的 for 循环值需要更多时间。

因此任何人都可以提供上述问题的解决方案吗?

【问题讨论】:

  • 也许 root 自己会将零发送给自己。尝试仅在成为 root 时接收

标签: c++ mpi


【解决方案1】:

这里的issues其实和2d里的issues类似:MPI_Type_create_subarray and MPI_Gather;那里有一个非常冗长的答案,涵盖了大部分关键点。

收集多维数组部分比只收集一维数组更棘手,因为您收集的数据实际上是重叠的。例如,排名 1 的第一行介于排名 0 的第一行和第二行之间。因此您需要 (a) 使用 mpi_gatherv,以便您可以指定位移,以及 (b) 显式设置数据类型的范围以方便重叠。

了解复杂数据结构的发送和接收(在 MPI 或其他任何形式中)就是要了解内存中数据的布局——这对于从代码中获得高性能至关重要。

说到内存布局,你的 Allocate3d 不适合这里的目的;问题是它分配的内存可能不连续。如果您以这种方式分配一个 10x10x10 数组,则无法保证元素 [1][0][0] 紧跟在元素 [0][9][9] 之后。这是 C/C++ 中的一个常见问题,它没有任何内置的多维数组概念。您需要执行以下操作:

void Allocate_3D_R(long double***& m, int d1, int d2, int d3) {

  m=new long double** [d1];
  for (int i=0; i<d1; ++i) {
    m[i]=new long double* [d2];
  }
  m[0][0] = new long double[d1*d2*d3];
  for (int i=0; i<d1; ++i) {
    for (int j=0; j<d2; ++j) {
      if (i!=0 && j!=0)
        m[i][j]=&(m[0][0][(i*d2+j)*d3];
      for (int k=0; k<d3; ++k) {
         m[i][j][k]=0.0;
      }
    }
  }

加号或减号——也就是说,您需要分配一个连续的 d1*d2*d3 内存块,然后将数组索引指向该连续内存中的适当位置。

【讨论】:

    猜你喜欢
    • 2017-08-20
    • 2011-02-28
    • 2016-04-04
    • 2012-12-01
    • 1970-01-01
    • 2013-07-04
    • 2014-04-20
    相关资源
    最近更新 更多