Fortran中的MPI发送接收问题

Fortran中的MPI发送接收问题,fortran,mpi,Fortran,Mpi,我现在开始为科学应用开发一个并行代码。我必须从p0到p1和从p1到p0交换一些缓冲区(我在处理器边界之间创建重影点) 错误可通过以下示例代码进行总结: program test use mpi implicit none integer id, ids, idr, ierr, tag, istat(MPI_STATUS_SIZE) real sbuf, rbuf call mpi_init(ierr) call MPI_COMM_RANK(MPI_COMM_WORLD,id,ierr)

我现在开始为科学应用开发一个并行代码。我必须从p0到p1和从p1到p0交换一些缓冲区(我在处理器边界之间创建重影点)

错误可通过以下示例代码进行总结:

program test
use mpi
implicit none

integer id, ids, idr, ierr, tag, istat(MPI_STATUS_SIZE)
real sbuf, rbuf

call mpi_init(ierr)

call MPI_COMM_RANK(MPI_COMM_WORLD,id,ierr)

if(id.eq.0) then
ids=0
idr=1
sbuf=1.5
tag=id
else    
ids=1
idr=0
sbuf=3.5
tag=id
endif

call mpi_send(sbuf,1,MPI_REAL,ids,tag,MPI_COMM_WORLD,ierr)

call mpi_recv(rbuf,1,MPI_REAL,idr,tag,MPI_COMM_WORLD,istat,ierr)

call mpi_finalize(ierr)
return
end

这有什么问题?

正如弗拉基米尔所指出的,您的代码太不完整,无法提供明确的答案

尽管如此,这可能是一个众所周知的错误

MPI\u Send()
可能会阻塞。从实用角度来看,
MPI_Send()
在发送短消息时可能会立即返回,但在发送大消息时可能会阻塞。注意:大小取决于MPI库、使用的互连以及其他运行时参数
MPI\u Send()
可能会阻塞,直到另一端发布了
MPI\u Recv()


似乎您在同一代码块中使用了
MPI\u Send()
MPI\u Recv()
,因此您可以尝试使用
MPI\u Sendrecv()
一次性完成此操作
MPI\u Sendrecv()
将在引擎盖下发出一个非阻塞发送,因此,如果您的问题确实是一个
MPI\u send()
死锁,这将有所帮助。

使用MPI编码一开始可能会很困难,最好您正在完成生成示例代码的步骤。您发布的示例代码由于死锁而挂起。两个进程都很忙,只有在MPI\u接收后才能完成发送。因此代码被卡住

解决这个问题有两种常见的方法

按特定顺序发送和接收 这是一个简单易懂的解决方案。对发送和接收操作进行编码,这样就不会有人被卡住。对于您的2进程测试用例,您可以执行以下操作:

if (id==0) then

   call mpi_send(sbuf,1,MPI_REAL,ids,tag,MPI_COMM_WORLD,ierr)
   call mpi_recv(rbuf,1,MPI_REAL,idr,tag,MPI_COMM_WORLD,istat,ierr)
else
   call mpi_recv(rbuf,1,MPI_REAL,idr,tag,MPI_COMM_WORLD,istat,ierr)
   call mpi_send(sbuf,1,MPI_REAL,ids,tag,MPI_COMM_WORLD,ierr)
endif
现在,进程1首先接收,因此不会出现死锁。这个特定的例子是不可扩展的,但是有各种各样的循环结构可以提供帮助。您可以想象一个例程将数据从每个进程发送到其他每个进程,如下所示:

do sending_process=1,nproc
   if (id == sending_process) then
      ! -- I am sending
      do destination_process = 1,nproc
         if (sending_process == destination_process) cycle
         call MPI_SEND ! Send to destination_process
      enddo
    elseif
       ! -- I am receiving
       call MPI_RECV ! Receive from sending_process
    endif
 enddo
这项工作相当好,并且易于遵循。我建议初学者使用这种结构

然而,对于真正的大问题,它有几个问题。您发送的消息数等于进程数的平方,这会使大型网络过载。此外,根据您的操作,您可能不需要将数据从每个进程发送到其他每个进程。(如果您提到了重影,我怀疑这对您是正确的。)您可以修改上述循环,使其仅在需要数据时发送,但对于这些情况,有一个更好的选择

使用非阻塞MPI操作 对于许多核心问题,这通常是最好的解决方案。我建议坚持使用简单的
MPI-ISEND
MPI-IRECV
。在这里,开始所有必要的发送和接收,然后等待。 在这里,我使用的是一些已经设置好的列表结构,它定义了每个进程所需目的地的完整列表

! -- Open sends
do d=1,Number_Destinations
   idest = Destination_List(d)

   call MPI_ISEND ! To destination d
enddo

! -- Open receives
do s=1,Number_Senders
   isend = Senders_List(s)
   call MPI_IRECV ! From source s
enddo

call MPI_WAITALL

此选项看起来可能更简单,但事实并非如此。您必须事先设置所有必要的列表,并且在缓冲区大小和数据对齐方面存在各种潜在问题。尽管如此,它通常是大型代码的最佳答案。

欢迎使用SO。你的问题不太清楚。请仔细阅读并在你的问题中添加a。你好,祖兰,对不起,我的问题不清楚,我会尽量简化一点。我有两个进程(秩=0和秩=1)。我需要交换一个从0到1的向量,同时交换一个从1到0的向量。我如何进行此通信?欢迎。你的代码太不完整了。我们需要看到一些我们可以编译和测试的东西。包括所有变量声明等。我建议您阅读以下内容:。您必须理解阻塞操作和非阻塞操作之间的区别。我已经提供了一个示例代码来重新创建问题。使用mpi_sendrecv,我再次得到一个块。谢谢你的标签不匹配,所以你的测试只能死锁。好的,但是因为我是mpi的noob,你能给我举个例子吗(如果你有时间的话)?同时,我会尽力按照你的建议去做!谢谢你可以为你的所有通信使用一个唯一的标签我不同意。根据我的经验,与单独的发送和接收相比,MPI_Sendrecv通常简化了代码,并避免了异步消息可能带来的混乱,特别是对于初学者。但这是基于观点的,所以我不会多说。当然,您必须始终像MPI_Send和MPI_Recv正在阻塞一样编写代码。亲爱的Ross,非常感谢!这是一个非常好的答案。明天我会试着跟着你的向导走!嗯,我正在为流体动力学建立一个代码(在系列作品中非常好)。因此,原则上,每个处理器都应该知道它的相邻单元,并且在每个时间步,必须用新的速度ecc值来更新这些重影单元。。。来自邻近的分区。我认为你们中的一些人应该适合这项工作。我也在CFD工作。您针对的问题大小(单元和处理器数量)是多少?这是针对非结构化网格的代码(网格的数据结构与openFoam相同)。我的目标通常是三维非定常流,所以让我们假设高达64个过程和百万个单元的顺序:)这是一个个人项目,我计划在未来的项目中使用它。但首先我需要一个并行化:)我想,第一个选项最适合你。它更容易调试和监视性能,并且它的局限性对于您的问题大小来说应该不是什么大问题。