我在调用 SUB EXC_MPI (MOD01) 时遇到了无效的内存引用,并且恰好在 MPI_StartAll 处(已注释)。
! ********** file mod01.f90 ************ !
MODULE MOD01
implicit none
include 'mpif.h'
! alternatively
! use mpi
! implicit none
PRIVATE
! ...
INTERFACE exc_mpi
MODULE PROCEDURE exc_mpi
END INTERFACE
PUBLIC exc_mpi
CONTAINS
subroutine exc_mpi (X)
!! send and receive from procs PN0 <-> PN1 and PN0 <-> PN2
real, dimension (ni:ns, m, l), intent(inout) :: X
logical, save :: frstime=.true.
integer, save :: mpitype_sn, mpitype_sp, mpitype_rn, mpitype_rp
integer, save :: requests(4), reqcount
integer :: istatus(MPI_STATUS_SIZE,4), ierr
if (frstime) then
call exc_init()
frstime = .false.
end if
call MPI_StartAll(reqcount,requests,ierr) !! <-- segfault here
call MPI_WaitAll(reqcount,requests,istatus,ierr)
return
contains
subroutine exc_init
integer :: i0, ierrs(12), ktag
reqcount = 0
ierrs=0
ktag = 1
! find i0
if ( condition1 ) then
! send to PN2
call MPI_Type_Vector(m*l, messlengthup(PN2), ns-ni+1, MPI_REAL, mpitype_sn, ierrs(1))
call MPI_Type_Commit(mpitype_sn, ierrs(3))
call MPI_Send_Init(X(i0, 1, 1), 1, mpitype_sn, PN2-1, ktag, MPI_COMM_WORLD, requests(reqcount+1), ierrs(5))
! recieve from PN2
call MPI_Type_Vector(m*l, messlengthdo(PN0), ns-ni+1, MPI_REAL, mpitype_rn, ierrs(2))
call MPI_Type_Commit(mpitype_rn,ierrs(4))
call MPI_Recv_Init(X(nend(irank)+1, 1, 1), 1, mpitype_rn, PN2-1, ktag+1, MPI_COMM_WORLD, requests(reqcount+2), ierrs(6))
reqcount = reqcount + 2
end if
if ( condition2 ) then
! send and rec PN0 <-> PN1
reqcount = reqcount + 2
end if
return
end subroutine exc_init
end subroutine exc_mpi
! ...
END MODULE MOD01
来电来自:
! ********** file mod02.f90 ************ !
MODULE MOD02
use MOD01, only: exc_mpi
IMPLICIT NONE
include 'mpif.h'
! alternatively
! use mpi
! implicit none
PRIVATE
! ...
INTERFACE MYSUB
MODULE PROCEDURE MYSUB
END INTERFACE
PUBLIC MYSUB
CONTAINS
SUBROUTINE MYSUB (Y)
IMPLICIT NONE
REAL, INTENT(INOUT) :: Y(nl:nr, m, l) ! ni<=nl, nr>=ns
REAL, ALLOCATABLE, DIMENSION(:,:,:) :: Y0
!...
allocate ( Y0(ni-1:ns, 1:m, 1:l) )
DO i = 1, icount
Y0(nl:nr,:,:) = Y(:,:,:)
call exc_mpi ( Y0(ni:ns, :, :) ) ! <-- segfault here
call mpi_barrier (mpi_comm_world, ierr)
Y0(ni-1,:,:) = 0.
CALL SUB01
END DO
deallocate (Y0)
RETURN
CONTAINS
SUBROUTINE SUB01
!...
FRE: DO iterm = 1, m
DIR: DO iterl = 1, l
DO itern = nl, nr
! Y(itern, iterm, iterl) = some_lin_combination(Y0)
END DO
END DO DIR
END DO FRE
END SUBROUTINE SUB01
! ...
END MODULE MOD02
当 MAIN(实际上是模块中的子)第二次调用 MYSUB(上面的代码)时,会在运行时引发分段错误。该错误不是系统性的,从某种意义上说,如果将作业拆分为一定数量的进程,例如 NPMAX,则程序可以工作,这取决于分解的数组。由于 procs 比 NPMAX 多,程序会出现段错误。更多环境条件:
- 几乎 [编译器 + mpi 独立]:[gfortran+ompi]、[gfortran+mpich] 和 [ifort+mpich] 出现同样的问题。
- 发生在基于 deb (glibc) 的 mini hpc 和 pc 上(“严重” nec、sun、ibm 上没有问题)
可以看出,MOD02 将不连续的切片数组 Y0 传递给交换过程 (MOD01)。我几乎可以克服故障(NPMAX 增长一个数量级)的唯一方法是来回交换尺寸,但这会导致执行速度减慢大约 2 倍。我敢打赌,永久交换 X/Y/Y0尺寸会解决,但我不想失去像 SUB01 这样的嵌套循环的效率(第一个尺寸比其他尺寸大得多)。
实际上,MOD02 创建了一个临时数组。明确地这样做并不能解决问题。
强制分配堆或堆栈并不能解决问题。
有什么提示吗?谢谢阅读
更新:在每次调用时初始化(在 sub exc_mpi 中的 if 语句中调用 exc_init() )确实可以解决,但由于 MAIN (未列出)循环很多,因此效率完全低下。
UPDATE2(在@Gilles 之后):即使传递一个连续的数组(在本例中为 Y1)并且 mpi 没有创建临时数组,此解决方法也不起作用。
SUBROUTINE MYSUB (Y)
IMPLICIT NONE
REAL, INTENT(INOUT) :: Y(nl:nr, m, l) ! ni<=nl, nr>=ns
REAL, ALLOCATABLE, DIMENSION(:,:,:) :: Y0, Y1
!...
allocate ( Y0(ni-1:ns, 1:m, 1:l) )
allocate ( Y1(ni:ns, 1:m, 1:l) )
DO i = 1, icount
Y1(nl:nr,:,:) = Y(:,:,:)
call exc_mpi ( Y0 ) ! <-- segfault here
call mpi_barrier (mpi_comm_world, ierr)
Y0(nl:nr,:,:) = Y1(nl:nr,:,:)
Y0(ni-1,:,:) = 0.
CALL SUB01
END DO
deallocate (Y1)
deallocate (Y0)
etc ...