【问题标题】:Is MPI_Gather the best choice?MPI_Gather 是最佳选择吗?
【发布时间】:2016-03-24 17:03:33
【问题描述】:

有 4 个进程,其中一个 (0) 是必须构建矩阵 C 的主进程,如下所示

-1  0  0 -1  0
 0 -1  0  0 -1
-1  1  1 -1  1
 1 -1  1  1 -1
-1  2  2 -1  2
 2 -1  2  2 -1
-1  3  3 -1  3
 3 -1  3  3 -1

为此,矩阵被声明为REAL, DIMENSION(:,:), ALLOCATABLE :: C 并分配给

IF (myid == 0) THEN
        ALLOCATE(C(2*nprocs,-2:+2))
END IF

其中nprocs 是进程数。进程 0 还设置了C = -1。对于我第一次尝试的交流方式

CALL MPI_GATHER((/0.0+myid,0.0+myid/),&
              & 2,MPI_REAL,&
              & C(:,0),&
              & 2,MPI_REAL,&
              & 0,MPI_COMM_WORLD,ieri)

填满中心栏,这很有效。 然后我尝试了

CALL MPI_GATHER((/myid, myid, myid, myid/),&
              & 4,MPI_REAL,&
              & (/C(1:2*nprocs:2,-1),C(2:2*nprocs:2,-2),C(1:2*nprocs:2,+2),C(2:2*nprocs:2,+1)/),&
              & 4,MPI_REAL,&
              & 0,MPI_COMM_WORLD,ierr)

填充其他列,但它不起作用,出现如下错误

Fortran runtime error: Index '1' of dimension 1 of array 'c' outside of expected range (140735073734712:140735073734712).

为了理解原因,我尝试用调用单独填充第一列

CALL MPI_GATHER((/0.0-myid/),&
              & 1,MPI_REAL,&
              & C(1:2*nprocs:2,-2),&
              & 1,MPI_REAL,&
              & 0,MPI_COMM_WORLD,ierr)

但或多或少发生了同样的事情。

我通过为所有进程分配C(即不管进程ID)解决了这个问题。为什么这会使调用起作用?

在此之后,我做了一些更改(在再次尝试一次填充所有列之前),只需将接收缓冲区放入 (/.../)

CALL MPI_GATHER((/0.0-myid/),&
              & 1,MPI_REAL,&
              & (/C(1:2*nprocs:2,-2)/),&
              & 1,MPI_REAL,&
              & 0,MPI_COMM_WORLD,ieri)

但这会使调用无效(没有错误,但C 中的一个元素都没有改变)。

希望有人能给我解释一下

  • 接收缓冲区中的构造函数(/.../)有什么问题?
  • 为什么必须在非根进程中分配接收缓冲区?
  • 需要使用mpi_gatherv来完成任务吗?
  • 有没有更好的方法来构建这样的矩阵?

编辑 是否可以使用 MPI 派生的数据类型来构建矩阵?

【问题讨论】:

  • 是的,可以使用派生类型,但这是一个新问题的主题。

标签: fortran mpi gfortran


【解决方案1】:

如果您还没有这样做,请首先使用use mpi 而不是include mpif.h。这可能会发现其中一些错误。

您不能将数组构造函数用作接收缓冲区。为什么?构造函数创建的数组是一个表达式。你不能在需要变量的地方使用它。

同样的方式你不能将1+1 传递给一个改变的子例程是参数。 1+1 是一个表达式,如果要更改,您需要一个变量。


其次,必须分配您写入或读取的每个数组。在 MPI_Gather 中,所有非根进程都忽略接收缓冲区。但是,当您从 C(1:2*nprocs:2,-2)C 之类的数组创建子数组时,必须分配这样的数组。这是 Fortran 的东西,而不是 MPI 的东西。


如果从每个等级收到的元素数量相同,您可以使用MPI_Gather,您不需要MPI_Gatherv


您可以考虑仅将数据接收到 1D 缓冲区并根据需要重新排序。另一种选择是沿最后一个维度分解它。

【讨论】:

  • 关于第一点和第三点好了(不能使用表达式作为参数,也不需要使用mpi_gatherv)。关于第二个,为进程0分配的矩阵C,那么问题出在哪里呢?最后,“沿最后一个维度分解”是什么意思?
  • 好吧,它是在第一个进程上分配的,但是你在所有进程中都创建了一个子数组!
  • 沿最后一个维度分解意味着类似 'C(-2:2,2*nprocs)' 或类似的东西。
  • 因为它只有在你通过合法的东西后才会被忽略。但它在你设法到达那里之前就崩溃了。制作未分配数组的子数组是根本不可能的。不要这样做。使用一些 if 条件或任何东西来避免它。
  • 您确实使用了所有这些。做 C(:) 正在制作数组的子数组。它正在使用数组,对于未分配的数组是非法的。
猜你喜欢
  • 1970-01-01
  • 2019-11-05
  • 2014-03-13
  • 1970-01-01
  • 2019-09-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多