1

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 (上記のコード) を 2 回目に呼び出すと、実行時にセグメンテーション違反が発生します。分解された配列に依存する NPMAX など、ジョブを特定の数のプロセスに分割する場合にプログラムが機能するという意味で、エラーは体系的ではありません。NPMAX よりも多くの proc を使用すると、プログラムはセグメンテーション違反を起こします。環境条件の詳細:

  • ほぼ [コンパイラ + mpi 独立]: [gfortran+ompi]、[gfortran+mpich]、[ifort+mpich] でも同じ問題が発生します。
  • deb ベース (glibc) の mini hpc と pcs で発生 (「深刻な」nec、sun、ibm では問題なし)

図からわかるように、MOD02 は交換手順 (MOD01) に、連続していないスライスされた配列 Y0 を渡します。この障害 (NPMAX が 1 桁大きくなる) をほぼ克服できる唯一の方法は、次元を前後に交換することですが、これにより実行速度が約 2 倍遅くなります。ディメンションは解決しますが、SUB01 のようなネストされたサイクルの効率を下げたくありません (最初のディメンションは他のディメンションよりもはるかに大きい)。

実際、MOD02 は一時配列を作成します。それを明示的に行っても問題は解決しません。

ヒープまたはスタックの割り当てを強制しても解決しません。

何かヒントはありますか?読んでくれてありがとう

更新: 呼び出しごとに初期化する (サブ 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 ...
4

0 に答える 0