我在理解MPI中阻塞通信和非阻塞通信的概念时遇到了麻烦。两者之间有什么区别?优点和缺点是什么?
Answers:
使用MPI_Send()
和阻止通信MPI_Recv()
。在通信完成之前,这些函数不会返回(即,它们将阻塞)。稍微简化一下,这意味着传递给的缓冲区MPI_Send()
可以重复使用,这是因为MPI将其保存在某个地方,或者是因为它已被目标接收。同样,MPI_Recv()
当接收缓冲区中已填充有效数据时,返回。
相反,使用MPI_Isend()
和进行非阻塞通信MPI_Irecv()
。即使通信尚未完成,这些函数也会立即返回(即,它们不会阻塞)。您必须致电MPI_Wait()
或MPI_Test()
查看通讯是否已完成。
阻塞通信已足够使用,因为它使用起来比较容易。在必要时使用无阻塞通信,例如,您可以先调用MPI_Isend()
,先进行一些计算,然后再进行MPI_Wait()
。这允许计算和通信重叠,这通常可以提高性能。
请注意,集体通信(例如全减少)仅在其阻塞版本(直到MPIv2)中可用。IIRC,MPIv3引入了非阻塞的集体通信。
MPI发送模式的快速概述可以在此处查看。
MPI_Send
当您可以重新使用缓冲区时,它就完成了,而与接收方是否已接收到数据(甚至根本没有发送数据)无关。
这篇文章虽然有点老,但我主张接受的答案。语句“这些功能要等到通信完成后才能返回”,这有点误导,因为阻塞通信无法保证发送和接收操作之间的任何握手。
第一个需要知道的是,发送具有四种通信模式:标准,缓冲,同步和就绪,并且每种通信都可以是阻塞的和非阻塞的
与发送不同,接收只有一种模式,可以是阻塞的或非阻塞的。
在继续进行之前,还必须明确一点,我明确提到了哪一个是MPI_Send \ Recv缓冲区,哪一个是系统缓冲区(这是MPI库拥有的每个处理器中的本地缓冲区,用于在通信各行之间移动数据。组)
阻止通信:阻止并不意味着消息已传递到接收方/目的地。它仅表示(发送或接收)缓冲区可供重用。要重用缓冲区,将信息复制到另一个存储区就足够了,即库可以将缓冲区数据复制到库中自己的存储位置,然后例如返回MPI_Send。
MPI标准非常清楚地将消息缓冲与发送和接收操作分离。邮件被缓存后,即使没有发布任何匹配的接收,阻塞发送也可以完成。但是在某些情况下,消息缓冲可能很昂贵,因此从发送缓冲区到接收缓冲区的直接复制可能是有效的。因此,MPI Standard提供了四种不同的发送模式,以使用户可以自由选择适合其应用程序的发送模式。让我们看一下每种通信模式中发生的情况:
1.标准模式
在标准模式下,是否缓冲传出消息取决于MPI库。在库决定缓冲传出消息的情况下,发送甚至可以在调用匹配的接收之前完成。在库决定不进行缓冲的情况下(出于性能原因或由于缓冲空间的不可用),在发布匹配的接收并且将发送缓冲区中的数据移至接收缓冲区之前,发送不会返回。
因此,在标准模式下的MPI_Send是非本地的,即无论是否已发布匹配的接收都可以启动以标准模式发送,并且其成功完成可能取决于匹配接收的发生(由于它是实现方式取决于消息是否将被缓冲)。
标准发送的语法如下:
int MPI_Send(const void *buf, int count, MPI_Datatype datatype,
int dest, int tag, MPI_Comm comm)
2.缓冲模式
像在标准模式下一样,可以启动缓冲模式下的发送,而不考虑匹配的接收已过帐的事实,并且发送可以在匹配的接收过帐之前完成。但是,主要区别是由于以下事实:如果发送已加注星标,并且没有发布任何匹配的接收,则必须对传出消息进行缓冲。请注意,如果发布了匹配的接收,则缓冲的发送可以与开始接收的处理器愉快地会合,但是如果没有接收,则处于缓冲模式的发送必须缓冲传出的消息以使发送完成。整体而言,缓冲的发送是local的。在这种情况下,缓冲区分配是用户定义的,如果缓冲区空间不足,则会发生错误。
缓冲区发送的语法:
int MPI_Bsend(const void *buf, int count, MPI_Datatype datatype,
int dest, int tag, MPI_Comm comm)
3.同步模式
在同步发送模式下,无论是否发布了匹配的接收,都可以开始发送。但是,仅当发布了匹配的接收并且接收方已开始接收通过同步发送发送的消息时,发送才能成功完成。同步发送的完成不仅表明发送缓冲区可以重复使用,而且表明接收过程已开始接收数据。如果发送和接收都处于阻塞状态,则在通信处理器集合点之前,通信不会在任一端完成。
同步发送的语法:
int MPI_Ssend(const void *buf, int count, MPI_Datatype datatype, int dest,
int tag, MPI_Comm comm)
4.就绪模式
与之前的三种模式不同,只有在匹配的接收已经过帐后,才能启动就绪模式的发送。发送的完成并不表示有关匹配接收的任何信息,而只是告诉发送缓冲区可以重用。使用就绪模式的发送与标准模式或同步模式具有相同的语义,并带有有关匹配接收的附加信息。可以用同步发送或标准发送替换具有通讯就绪模式的正确程序,而除了性能差异外,对结果没有任何影响。
准备发送的语法:
int MPI_Rsend(const void *buf, int count, MPI_Datatype datatype, int dest,
int tag, MPI_Comm comm)
在经历了全部4次阻塞发送之后,它们在原理上可能看起来有所不同,但是根据实现方式,一种模式的语义可能与另一种模式相似。
例如,MPI_Send通常是阻塞模式,但是根据实现方式,如果消息大小不是太大,则MPI_Send会将传出消息从发送缓冲区复制到系统缓冲区(在现代系统中通常是这样)并立即返回。让我们看下面的例子:
//assume there are 4 processors numbered from 0 to 3
if(rank==0){
tag=2;
MPI_Send(&send_buff1, 1, MPI_DOUBLE, 1, tag, MPI_COMM_WORLD);
MPI_Send(&send_buff2, 1, MPI_DOUBLE, 2, tag, MPI_COMM_WORLD);
MPI_Recv(&recv_buff1, MPI_FLOAT, 3, 5, MPI_COMM_WORLD);
MPI_Recv(&recv_buff2, MPI_INT, 1, 10, MPI_COMM_WORLD);
}
else if(rank==1){
tag = 10;
//receive statement missing, nothing received from proc 0
MPI_Send(&send_buff3, 1, MPI_INT, 0, tag, MPI_COMM_WORLD);
MPI_Send(&send_buff3, 1, MPI_INT, 3, tag, MPI_COMM_WORLD);
}
else if(rank==2){
MPI_Recv(&recv_buff, 1, MPI_DOUBLE, 0, 2, MPI_COMM_WORLD);
//do something with receive buffer
}
else{ //if rank == 3
MPI_Send(send_buff, 1, MPI_FLOAT, 0, 5, MPI_COMM_WORLD);
MPI_Recv(recv_buff, 1, MPI_INT, 1, 10, MPI_COMM_WORLD);
}
让我们看一下上面例子中每个等级的情况
等级0 试图发送到等级1和等级2,并从等级1和d 3接收。
等级1试图发送至等级0和等级3,但未从任何其他等级接收任何东西
等级2试图从等级0接收,然后对recv_buff中接收的数据进行一些操作。
等级3试图发送到等级0并从等级1接收
初学者感到困惑的是,等级0正在发送至等级1,但等级1尚未开始任何接收操作,因此通信应阻塞或停止,等级0中的第二个send语句根本不应执行(这就是MPI文档强调,这是实现定义的,是否将缓冲传出的消息)。在大多数现代系统中,此类小消息(大小为1)很容易被缓冲,MPI_Send将返回并执行其下一个MPI_Send语句。因此,在上面的示例中,即使等级1中的接收未开始,等级0中的第一个MPI_Send也将返回并执行下一条语句。
在假设的情况下,等级3在等级0之前开始执行,它将在第一个send语句中将传出消息从发送缓冲区复制到系统缓冲区(在现代系统中;)),然后开始执行其接收语句。等级0完成其两个send语句并开始执行其接收语句后,系统将等级3缓存在系统中的数据复制到等级0的接收缓冲区中。
如果在处理器中开始了接收操作并且没有发布任何匹配的发送,则该过程将阻塞,直到接收缓冲区中填充了期望的数据。在这种情况下,除非返回了MPI_Recv,否则将阻止/停止计算或其他MPI通信。
了解了缓冲现象之后,应该回过头来思考更多有关MPI_Ssend的问题,它具有阻塞通信的真实语义。即使MPI_Ssend将传出消息从发送缓冲区复制到系统缓冲区(再次由实现定义),也必须注意,除非发送处理器已收到来自接收进程的某些确认(低级格式),否则MPI_Ssend将不会返回。
幸运的是,MPI决定让用户更容易接收,并且在阻塞通信中只有一个接收:MPI_Recv,并且可以与上述四种发送模式中的任何一种一起使用。对于MPI_Recv,阻塞意味着接收仅在其缓冲区中包含数据后才返回。这意味着接收只能在匹配的发送开始之后才能完成,但并不意味着它可以在匹配的发送完成之前完成。
在此类阻塞调用期间发生的事情是,计算将暂停直到释放阻塞的缓冲区。这通常会导致计算资源的浪费,因为Send / Recv通常会将数据从一个存储位置复制到另一个存储位置,而cpu中的寄存器保持空闲状态。
非阻塞通信:对于非阻塞通信,应用程序创建用于通信为发送请求和/或接收和回来的手柄,然后终止。这就是确保执行该过程所需的全部。即,MPI库被通知必须执行该操作。
对于发送方,这允许通信进行重叠计算。
对于接收方,这允许重叠一部分通信开销,即将消息直接复制到应用程序中接收方的地址空间中。
在使用阻塞通信时,您必须注意发送和接收呼叫,例如查看以下代码
if(rank==0)
{
MPI_Send(x to process 1)
MPI_Recv(y from process 1)
}
if(rank==1)
{
MPI_Send(y to process 0);
MPI_Recv(x from process 0);
}
在这种情况下会发生什么?
MPI_Ssend
严格阻止,因为它会返回直到目标接收到消息为止。以下链接说明了不同的供应商选择了不同的实现。mcs.anl.gov/research/projects/mpi/sendmode.html