此问题遵循 MPI_type_create_subarray和MPI_Gather 上的现有主题。我的目标是使用MPI_Type_Create_Subarray和Fortran 90中的MPI_Gatherv将所有从属进程(数量为4个)的更大数组的子数组收集到主进程(rank = 0)上的更大数组中。这有助于我理解MPI_Gatherv用于我的其他项目。以下是我的示例代码:
program main
implicit none
include "mpif.h"
integer :: ierr, myRank, nProcs
integer :: sendsubarray, recvsubarray, resizedrecvsubarray
integer, dimension(2) :: starts,sizes,subsizes
integer, dimension(:), allocatable :: counts, disps
integer, parameter :: nx_glb=10, ny_glb=10, nx=5, ny=5
integer, dimension(:,:), target, allocatable :: mat, matG
integer, pointer :: sendPtr(:,:), recvPtr(:,:)
integer :: i, j
call mpi_init(ierr)
call mpi_comm_rank(mpi_comm_world, myRank, ierr)
call mpi_comm_size(mpi_comm_world, nProcs, ierr)
sizes(1)=nx+2; sizes(2)=ny+2
subsizes(1)=nx; subsizes(2)=ny
starts(1)=2; starts(2)=2
call mpi_type_create_subarray(2, sizes, subsizes, starts, mpi_order_fortran, &
mpi_integer, sendsubarray, ierr)
call mpi_type_commit(sendsubarray,ierr)
allocate(mat(1:nx+2,1:ny+2))
do j=1, ny+2
do i=1, nx+2
if(i.eq.1 .or. i.eq.nx+2 .or. j.eq.1 .or. j.eq.ny+2) then
mat(i,j)=1000
else
mat(i,j) = myRank
end if
end do
end do
sendPtr=>mat
if(myRank.eq.0) then
allocate(matG(nx_glb,ny_glb))
matG=1000
sizes(1)=nx_glb; sizes(2)=ny_glb
subsizes(1)=nx; subsizes(2)=ny
starts(1)=1; starts(2)=1
call mpi_type_create_subarray(2, sizes, subsizes, starts, mpi_order_fortran, &
mpi_integer, recvsubarray, ierr)
call mpi_type_commit(recvsubarray, ierr)
call mpi_type_create_resized(recvsubarray, 1, sizeof(i), resizedrecvsubarray, ierr)
call mpi_type_commit(resizedrecvsubarray,ierr)
recvPtr=>matG
end if
counts(1:4) = (/1, 1, 1, 1/)
disps(1:4) = (/0, 5, 50, 55/)
call mpi_gatherv(sendPtr,1,sendsubarray,recvPtr,counts,disps,resizedrecvsubarray, &
0,mpi_comm_world,ierr)
if(myRank.eq.0) then
do i=1, nx_glb
write(1000,*) (matG(i,j),j=1, ny_glb)
end do
end if
call mpi_finalize(ierr)
end program main
但是,执行此代码会产生forrtl: severe(174): SIGSEGV, segmentation fault occurred
。
似乎我试图指向一个在收集时未初始化或声明的数组的变量/位置。我试图以多种方式进行调试,但徒劳无功。
非常感谢提前。
答案 0 :(得分:1)
当你看到这里的主要问题时,你会踢自己;你没有分配计数或分配。
顺便说一句,我强烈建议您使用use mpi
而不是include mpif.h
; use语句(在隐式none之前)引入了具有更好的类型检查的F90接口。当您这样做时,您还会看到,对于您的类型创建已调整大小,您需要kind
mpi_address_kind
的整数。
更新:
好的,所以对于如何进行gatherv这个更大的问题,你的事情基本上是对的,但是你是对的,启动,disps等必须是零索引,而不是1,因为实际的MPI库是从C的角度来看,即使使用FORTRAN绑定也是如此。所以对于sentubarray,start必须是[1,1]
;对于recv子阵列,它必须是[0,0]
,并且resize,start必须为0且extent必须是sizeof(type)(并且这两者必须是mpi_address_kind类型的整数)。
我正在使用这些更新附加您的代码版本,并且底层数组具有类型字符,因此更容易打印出诊断并查看正在发生的事情:
program main
use mpi
implicit none
integer :: ierr, myRank, nProcs
integer :: sendsubarray, recvsubarray, resizedrecvsubarray
integer, dimension(2) :: starts,sizes,subsizes
integer, dimension(:), allocatable :: counts, disps
integer, parameter :: nx_glb=10, ny_glb=10, nx=5, ny=5
character, dimension(:,:), target, allocatable :: mat, matG
character :: c
integer :: i, j, p
integer(kind=mpi_address_kind) :: start, extent
call mpi_init(ierr)
call mpi_comm_rank(mpi_comm_world, myRank, ierr)
call mpi_comm_size(mpi_comm_world, nProcs, ierr)
sizes(1)=nx+2; sizes(2)=ny+2
subsizes(1)=nx; subsizes(2)=ny
starts(1)=1; starts(2)=1
call mpi_type_create_subarray(2, sizes, subsizes, starts, mpi_order_fortran, &
mpi_character, sendsubarray, ierr)
call mpi_type_commit(sendsubarray,ierr)
allocate(mat(1:nx+2,1:ny+2))
mat='.'
forall (i=2:nx+1,j=2:ny+1) mat(i,j)=ACHAR(ICHAR('0')+myRank)
if(myRank.eq.0) then
allocate(matG(nx_glb,ny_glb))
matG='.'
sizes(1)=nx_glb; sizes(2)=ny_glb
subsizes(1)=nx; subsizes(2)=ny
starts(1)=0; starts(2)=0
call mpi_type_create_subarray(2, sizes, subsizes, starts, mpi_order_fortran, &
mpi_character, recvsubarray, ierr)
call mpi_type_commit(recvsubarray, ierr)
extent = sizeof(c)
start = 0
call mpi_type_create_resized(recvsubarray, start, extent, resizedrecvsubarray, ierr)
call mpi_type_commit(resizedrecvsubarray,ierr)
end if
allocate(counts(4),disps(4))
counts(1:4) = (/1, 1, 1, 1/)
disps(1:4) = (/0, 5, 50, 55/)
call mpi_gatherv(mat,1,sendsubarray,matG,counts,disps,resizedrecvsubarray, &
0,mpi_comm_world,ierr)
do p=0,nProcs
if (myRank == p) then
print *, 'Local array for rank ', myRank
do i=1, nx+2
print *, (mat(i,j),j=1,ny+2)
end do
endif
call MPI_Barrier(MPI_COMM_WORLD,ierr)
enddo
if(myRank.eq.0) then
print *, 'Global array: '
do i=1, nx_glb
print *, (matG(i,j),j=1, ny_glb)
end do
end if
call mpi_finalize(ierr)
end program main
输出:
Local array for rank 0
.......
.00000.
.00000.
.00000.
.00000.
.00000.
.......
Local array for rank 1
.......
.11111.
.11111.
.11111.
.11111.
.11111.
.......
Local array for rank 2
.......
.22222.
.22222.
.22222.
.22222.
.22222.
.......
Local array for rank 3
.......
.33333.
.33333.
.33333.
.33333.
.33333.
.......
Global array:
0000022222
0000022222
0000022222
0000022222
0000022222
1111133333
1111133333
1111133333
1111133333
1111133333
......有意义吗?这非常类似于这个问题的C版本,这里已经回答了问题(MPI_Type_create_subarray and MPI_Gather),但你已经把事情大部分都解决了......
哦,是的,还有一件事 - 你实际上并不需要在Fortran中设置指向send / recv数据的指针。在C中,您需要显式地将指针传递给数据数组;在fortran中,你可以传递数组(并且它们已经通过引用传递,例如相当于C传递指向变量的指针)。所以你可以传递数组。