【问题标题】:MPI in Fortran gives garbage valuesFortran 中的 MPI 给出垃圾值
【发布时间】:2017-08-14 11:51:03
【问题描述】:
PROGRAM ShareNeighbors
IMPLICIT REAL (a-h,o-z)
INCLUDE "mpif.h"
PARAMETER (m = 500, n = 500)
DIMENSION a(m,n), b(m,n)
DIMENSION h(m,n)
INTEGER istatus(MPI_STATUS_SIZE)
INTEGER iprocs, jprocs 
PARAMETER (ROOT = 0) 
integer dims(2),coords(2)
logical   periods(2)
data periods/2*.false./
integer status(MPI_STATUS_SIZE)
integer comm2d,req,source

CALL MPI_INIT(ierr)
CALL MPI_COMM_SIZE(MPI_COMM_WORLD, nprocs, ierr)
CALL MPI_COMM_RANK(MPI_COMM_WORLD, myrank, ierr)
! Get a new communicator for a decomposition of the domain.  
! Let MPI find a "good" decomposition
dims(1) = 0
dims(2) = 0
CALL MPI_DIMS_CREATE(nprocs,2,dims,ierr)
if (myrank.EQ.Root) then
   print *,nprocs,'processors have been arranged into',dims(1),'X',dims(2),'grid'
endif
CALL MPI_CART_CREATE(MPI_COMM_WORLD,2,dims,periods,.true., &
                  comm2d,ierr)
!   Get my position in this communicator
CALL MPI_COMM_RANK(comm2d,myrank,ierr)
! Get the decomposition
CALL fnd2ddecomp(comm2d,m,n,ista,iend,jsta,jend)
! print *,ista,jsta,iend,jend
ilen = iend - ista + 1
jlen = jend - jsta + 1

CALL MPI_Cart_get(comm2d,2,dims,periods,coords,ierr)
iprocs = dims(1)
jprocs = dims(2)
myranki = coords(1)
myrankj = coords(2)

DO j = jsta, jend
    DO i = ista, iend
    a(i,j) = myrank+1
    ENDDO
ENDDO
! Send data from each processor to Root
call MPI_ISEND(ista,1,MPI_INTEGER,Root,1,  &
                  MPI_COMM_WORLD,req,ierr)
call MPI_ISEND(iend,1,MPI_INTEGER,Root,1,  &
                  MPI_COMM_WORLD,req,ierr)
call MPI_ISEND(jsta,1,MPI_INTEGER,Root,1,  &
                  MPI_COMM_WORLD,req,ierr)
call MPI_ISEND(jend,1,MPI_INTEGER,Root,1,  &
                  MPI_COMM_WORLD,req,ierr)
call MPI_ISEND(a(ista:iend,jsta:jend),(ilen)*(jlen),MPI_REAL, &
                  Root,1,MPI_COMM_WORLD,req,ierr )
!    Recieved the results from othe precessors   
if (myrank.EQ.Root) then
    do source = 0,nprocs-1
       call MPI_RECV(ista,1,MPI_INTEGER,source,   &
                     1,MPI_COMM_WORLD,status,ierr )
       call MPI_RECV(iend,1,MPI_INTEGER,source,   &
                     1,MPI_COMM_WORLD,status,ierr )
       call MPI_RECV(jsta,1,MPI_INTEGER,source,   &
                     1,MPI_COMM_WORLD,status,ierr )
       call MPI_RECV(jend,1,MPI_INTEGER,source,   &
                    1,MPI_COMM_WORLD,status,ierr )      
        ilen = iend - ista + 1
        jlen = jend - jsta + 1                          
       call MPI_RECV(a(ista:iend,jsta:jend),(ilen)*(jlen),MPI_REAL,   &
                    source,1,MPI_COMM_WORLD,status,ierr)
! print the results
       call ZMINMAX(m,n,ista,iend,jsta,jend,a(:,:),amin,amax)
       print *, 'myid=',source,amin,amax
        call MPI_Wait(req, status, ierr) 
   enddo    
endif

CALL MPI_FINALIZE(ierr)
END

subroutine fnd2ddecomp(comm2d,m,n,ista,iend,jsta,jend)
integer   comm2d
integer   m,n,ista,jsta,iend,jend
integer   dims(2),coords(2),ierr
logical   periods(2)
! Get (i,j) position of a processor from Cartesian topology.
CALL MPI_Cart_get(comm2d,2,dims,periods,coords,ierr)
! Decomposition in first (ie. X) direction
CALL MPE_DECOMP1D(m,dims(1),coords(1),ista,iend)
! Decomposition in second (ie. Y) direction
CALL MPE_DECOMP1D(n,dims(2),coords(2),jsta,jend)

return
end
SUBROUTINE MPE_DECOMP1D(n,numprocs,myid,s,e)
integer n,numprocs,myid,s,e,nlocal,deficit
nlocal  = n / numprocs
s       = myid * nlocal + 1
deficit = mod(n,numprocs)
s       = s + min(myid,deficit)
! Give one more slice to processors
if (myid .lt. deficit) then
    nlocal = nlocal + 1
endif
e = s + nlocal - 1
if (e .gt. n .or. myid .eq. numprocs-1) e = n

return
end
SUBROUTINE ZMINMAX(IX,JX,SX,EX,SY,EY,ZX,ZXMIN,ZXMAX)

INTEGER :: IX,JX,SX,EX,SY,EY
REAL :: ZX(IX,JX)
REAL :: ZXMIN,ZXMAX

ZXMIN=1000.
ZXMAX=-1000.
DO II=SX,EX
   DO JJ=SY,EY  
      IF(ZX(II,JJ).LT.ZXMIN)ZXMIN=ZX(II,JJ)
      IF(ZX(II,JJ).GT.ZXMAX)ZXMAX=ZX(II,JJ)
   ENDDO
ENDDO   

RETURN
END

当我使用 4 个处理器运行上述代码时,Root 会收到垃圾值。至于15个处理器,数据传输是正确的。我该如何解决这个问题? 我想这是相关的缓冲区,我不清楚这一点。我必须如何明智地处理缓冲区?

【问题讨论】:

  • 向我们展示这些值并解释为什么它们是错误的以及您希望看到哪些值。
  • 我建议您将子例程设置为内部(将它们放在包含后面)或将它们放入模块中,并使用use mpi 而不是INCLUDE "mpif.h"。这将使编译器可以为您执行大量检查。

标签: fortran mpi


【解决方案1】:

1.问题

您正在执行多次发送

call MPI_ISEND(ista,1,MPI_INTEGER,Root,1,  &
                  MPI_COMM_WORLD,req,ierr)
call MPI_ISEND(iend,1,MPI_INTEGER,Root,1,  &
                  MPI_COMM_WORLD,req,ierr)
call MPI_ISEND(jsta,1,MPI_INTEGER,Root,1,  &
                  MPI_COMM_WORLD,req,ierr)
call MPI_ISEND(jend,1,MPI_INTEGER,Root,1,  &
                  MPI_COMM_WORLD,req,ierr)
call MPI_ISEND(a(ista:iend,jsta:jend),(ilen)*(jlen),MPI_REAL, &
                  Root,1,MPI_COMM_WORLD,req,ierr )

它们都具有相同的请求变量req。那是行不通的。

2。问题

您正在非阻塞 MPI 中使用子数组 a(ista:iend,jsta:jend)。这是不允许的*。您需要将数组复制到某个临时数组缓冲区或使用 MPI 派生的子数组数据类型(在这个阶段对您来说太难了)。

问题的原因是编译器会为调用ISend 创建一个临时副本。 ISend 会记住地址,但不会发送任何内容。然后临时被删除,地址变为无效。然后MPI_Wait 将尝试使用该地址并失败。

3.问题

您的MPI_Wait 放错地方了。它必须在任何 if 条件的发送之后,以便始终执行它们(前提是您始终发送)。

您必须单独收集所有请求,而不是等待所有请求。最好将它们放在一个数组中,然后使用 MPI_Waitall 一次等待所有这些。

请记住,如果缓冲区很大,ISend 通常不会实际发送任何内容。交换经常发生在Wait 操作期间。至少对于更大的数组。


推荐:

举一个简单的问题示例,尝试在两个进程之间只用 MPI_IRecv 和 MPI_ISend 交换两个小数组。尽可能简单的测试问题。从中学习,做简单的步骤。没有冒犯,但你目前对非阻塞 MPI 的理解太弱,无法编写完整的程序。 MPI 很难,非阻塞 MPI 更难。


* 使用 MPI-2 中可用的接口时不允许。 MPI-3 通过在可能的情况下使用use mpi_f08 带来了一个可用的新接口。但请先学习基础知识。

【讨论】:

  • 我也照你说的做了。在发送后添加 mpi_Wait 会导致死锁,其中 mpi_recv 运行后的 mpi_wait 。但问题是一样的,处理器的垃圾值小于 16。
  • 这是因为您必须正确订购接收和发送。我不能教你一切。最好的办法是将所有接收都设为IRecv,收集来自发送和接收的所有请求,然后创建一个MPI_Waitall
  • 但是如果您不花一些时间思考哪个进程在做什么以及您应该如何订购东西,那么死锁极有可能。拿一支笔和一张纸画出少量进程的通信。你真的必须考虑一下。 MPI 很难。我已经在您之前的一个问题中告诉过您,您应该通过简单的小示例学习MPI_ISendMPI_Irecv。这仍然是真的。
  • 一开始的提示:MPI_Recv 不会完成,除非真的有东西向它发送数据。除非有相应的 Recv 或来自 Irecv 的等待正在运行,否则来自 ISend 的 MPI_Wait 不会完成。你必须正确地订购东西。画一张图,哪个进程在做什么。从 2 个流程开始。在你做对后添加更多。
  • 可能值得一提的是,将数组切片传递给非阻塞操作仅对旧 MPI 接口存在问题。 MPI-3.0 中的mpi_f08 模块接口使用了新的 Fortran 2008 功能,允许编译器直接传递切片并消除限制。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-06
  • 2021-02-20
  • 2015-02-28
相关资源
最近更新 更多