Fortran中的MPI发送接收问题
我现在开始为科学应用开发一个并行代码。我必须从p0到p1和从p1到p0交换一些缓冲区(我在处理器边界之间创建重影点) 错误可通过以下示例代码进行总结: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)
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个过程和百万个单元的顺序:)这是一个个人项目,我计划在未来的项目中使用它。但首先我需要一个并行化:)我想,第一个选项最适合你。它更容易调试和监视性能,并且它的局限性对于您的问题大小来说应该不是什么大问题。