MPI_Gather是最好的选择吗?
Is MPI_Gather the best choice?
有 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 派生数据类型来构建矩阵?
如果您还没有这样做,请首先使用 use mpi
而不是 include mpif.h
。这可能会发现其中一些错误。
您不能将数组构造函数用作接收缓冲区。为什么?构造函数创建的数组是一个表达式。您不能在需要变量的地方使用它。
与您不能将 1+1
传递给更改为参数的子例程的方式相同。 1+1
是一个表达式,如果要更改它,则需要一个变量。
其次,必须分配您写入或读取的每个数组。在 MPI_Gather 中,所有非根进程的接收缓冲区都将被忽略。但是,当您从 C(1:2*nprocs:2,-2)
从 C
之类的数组创建子数组时,必须分配这样的数组。这是 Fortran 的东西,不是 MPI 的。
如果从每个等级接收到的元素数量相同你可以使用MPI_Gather
,你不需要MPI_Gatherv
。
您可以考虑只将数据接收到一维缓冲区中并根据需要重新排序。另一种选择是沿最后一个维度分解它。
有 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 派生数据类型来构建矩阵?
如果您还没有这样做,请首先使用 use mpi
而不是 include mpif.h
。这可能会发现其中一些错误。
您不能将数组构造函数用作接收缓冲区。为什么?构造函数创建的数组是一个表达式。您不能在需要变量的地方使用它。
与您不能将 1+1
传递给更改为参数的子例程的方式相同。 1+1
是一个表达式,如果要更改它,则需要一个变量。
其次,必须分配您写入或读取的每个数组。在 MPI_Gather 中,所有非根进程的接收缓冲区都将被忽略。但是,当您从 C(1:2*nprocs:2,-2)
从 C
之类的数组创建子数组时,必须分配这样的数组。这是 Fortran 的东西,不是 MPI 的。
如果从每个等级接收到的元素数量相同你可以使用MPI_Gather
,你不需要MPI_Gatherv
。
您可以考虑只将数据接收到一维缓冲区中并根据需要重新排序。另一种选择是沿最后一个维度分解它。