mpi:阻塞与非阻塞

lam*_*mba 60 parallel-processing cluster-computing mpi

我无法理解阻止MPI中的通信和非阻塞通信的概念.两者有什么不同?有哪些优点和缺点?

谢谢!

use*_*136 83

阻止通信使用MPI_Send()和完成MPI_Recv().在通信结束之前,这些功能不会返回(即,它们会阻塞).稍微简化一下,这意味着传递给的缓冲区MPI_Send()可以重复使用,因为MPI将其保存在某处,或者因为它已被目的地接收.同样,MPI_Recv()当接收缓冲区填充有效数据时返回.

相反,使用MPI_Isend()和完成非阻塞通信MPI_Irecv().即使通信尚未完成,这些功能也会立即返回(即,它们不会阻止).您必须致电MPI_Wait()MPI_Test()查看通讯是否已完成.

当它足够时使用阻塞通信,因为它更容易使用.必要时使用非阻塞通信,例如,您可以调用MPI_Isend(),进行一些计算,然后执行MPI_Wait().这允许计算和通信重叠,这通常导致改进的性能.

请注意,集合通信(例如,all-reduce)仅在其阻塞版本中可用,直到MPIv2.IIRC,MPIv3引入了非阻塞集体通信.

这里可以看到MPI发送模式的快速概述.

  • @ user1202136:您可能想要提到"MPI_Send"在您可以重用缓冲区时完成,无论接收方是否已收到数据(甚至数据是否已经发送). (14认同)
  • 所以MPI_Send()与MPI_Isend()+ MPI_Wait()相同吗? (2认同)
  • 是的,除了开销,您可以将 MPI_Send() 视为 MPI_Isend() 后跟 MPI_Wait()。 (2认同)

ggu*_*lia 34

这篇文章虽然有点陈旧,但我认为接受了答案.声明"这些函数在通信完成之前不会返回"有点误导,因为阻塞通信并不能保证对发送和接收操作进行任何握手.

首先需要知道,send有四种通信模式:标准,缓冲,同步就绪,每种模式都可以阻塞非阻塞

与发送不同,接收只有一种模式,可以是阻塞非阻塞.

在继续之前,还必须明确我明确提到哪一个是MPI_Send\Recv缓冲区,哪一个是系统缓冲区(这是MPI库拥有的每个处理器中的本地缓冲区,用于在通信等级之间移动数据)组)

阻塞通信:阻止并不意味着消息已传送到接收方/目的地.它只是意味着(发送或接收)缓冲区可以重用.为了重用缓冲区,将信息复制到另一个存储区域就足够了,即库可以将缓冲区数据复制到库中的自己的存储位置,然后,例如,MPI_Send可以返回.

MPI标准非常清楚地将消息缓冲与发送和接收操作分离.一旦消息被缓冲,阻塞发送就可以完成,即使没有发布匹配的接收.但在某些情况下,消息缓冲可能很昂贵,因此从发送缓冲区直接复制到接收缓冲区可能是有效的.因此,MPI标准提供了四种不同的发送模式,使用户可以自由选择适合其应用的发送模式.让我们来看看每种沟通方式会发生什么:

1.标准模式

标准模式下,无论是否缓冲传出消息,都由MPI库决定.在库决定缓冲传出消息的情况下,即使在调用匹配的接收之前,也可以完成发送.在库决定不缓冲(由于性能原因,或由于缓冲区空间不可用)的情况下,发送将不会返回,直到匹配的接收已发布并且发送缓冲区中的数据已移至接收缓冲区.

因此,标准模式下的MPI_Send是非本地的,即无论是否已发布匹配的接收,都可以开始以标准模式发送,并且其成功完成可能取决于匹配接收的发生(由于它是实现的事实)依赖于消息是否将被缓冲).

标准发送的语法如下:

int MPI_Send(const void *buf, int count, MPI_Datatype datatype, 
             int dest, int tag, MPI_Comm comm)
Run Code Online (Sandbox Code Playgroud)

2.缓冲模式

与标准模式类似,可以启动缓冲模式下的发送,而不管匹配的接收是否已发布,并且发送可能在匹配的接收已发布之前完成.然而,主要的区别在于,如果发送被盯着并且没有发布匹配的接收,则必须缓冲外发消息.请注意,如果匹配的接收被发布,则缓冲的发送可以愉快地与开始接收的处理器会合,但是如果没有接收,则缓冲模式下的发送必须缓冲传出消息以允许发送完成.完整地,缓冲发送是本地的.在这种情况下,缓冲区分配是用户定义的,如果缓冲区空间不足,则会发生错误.

缓冲区发送的语法:

int MPI_Bsend(const void *buf, int count, MPI_Datatype datatype,
             int dest, int tag, MPI_Comm comm)
Run Code Online (Sandbox Code Playgroud)

3.同步模式

在同步发送模式下,无论是否发布匹配的接收,都可以开始发送.但是,只有在发布匹配的接收并且接收器已开始接收同步发送的消息时,发送才会成功完成.同步发送的完成不仅表明发送中的缓冲区可以被重用,而且表明接收进程已经开始接收数据.如果发送和接收都是阻塞的,那么通信在通信处理器会合之前的任何一端都不会完成.

同步发送的语法:

int MPI_Ssend(const void *buf, int count, MPI_Datatype datatype, int dest,
              int tag, MPI_Comm comm)
Run Code Online (Sandbox Code Playgroud)

4.就绪模式

与前三种模式不同,只有匹配的接收已经发布,才能启动就绪模式.发送的完成并不表示匹配接收的任何内容,只是告诉发送缓冲区可以重用.使用就绪模式的发送具有与标准模式相同的语义,或具有与匹配接收有关的附加信息的同步模式.具有就绪通信模式的正确程序可以用同步发送或标准发送替换,除了性能差异之外对结果没有影响.

准备发送的语法:

int MPI_Rsend(const void *buf, int count, MPI_Datatype datatype, int dest, 
              int tag, MPI_Comm comm)
Run Code Online (Sandbox Code Playgroud)

经历了所有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);
}
Run Code Online (Sandbox Code Playgroud)

让我们看看上面例子中每个等级发生的事情

等级0 试图发送到等级1和等级2,并从等级1和等级3接收.

等级1试图发送到等级0和等级3并且不从任何其他等级接收任何东西

等级2试图从等级0接收,然后对recv_buff中接收的数据进行一些操作.

等级3试图发送到等级0并从等级1接收

初学者感到困惑的是,等级0发送到等级1,但等级1没有开始任何接收操作,因此通信应该阻塞或停止,并且等级0中的第二个发送语句不应该被执行(这就是MPI文档强调它是实现定义是否将缓冲传出消息).在大多数现代系统中,这种小尺寸的消息(此处大小为1)将很容易被缓冲,MPI_Send将返回并执行它的下一个MPI_Send语句.因此,在上面的示例中,即使未开始等级1中的接收,等级0中的第一MPI_Send将返回并且将执行其下一个语句.

在排名3在排名0之前开始执行的假设情况下,它将第一个发送语句中的传出消息从发送缓冲区复制到系统缓冲区(在现代系统中;))然后开始执行其接收语句.一旦等级0完成其两个发送语句并开始执行其接收语句,则系统中按等级3缓冲的数据被复制到等级0的接收缓冲器中.

如果在处理器中启动了接收操作并且没有发布匹配的发送,则该过程将阻塞,直到接收缓冲区充满了它期望的数据.在这种情况下,除非MPI_Recv已返回,否则将阻止/暂停计算或其他MPI通信.

理解了缓冲现象之后,应该返回并更多地思考MPI_Ssend,它具有阻塞通信的真实语义.即使MPI_Ssend将传出消息从发送缓冲区复制到系统缓冲区(也是实现定义的),必须注意MPI_Ssend将不会返回,除非发送处理器已经接收到来自接收进程的某些确认(低级格式).

幸运的是,MPI决定在接收方面为用户提供更轻松的功能,并且在阻塞通信中只有一个接收:MPI_Recv,并且可以与上述四种发送模式中的任何一种一起使用.对于MPI_Recv,阻塞意味着只有在其包含缓冲区中的数据后才接收返回.这意味着只有在匹配的发送开始后才能完成接收,但并不意味着在匹配的发送完成之前它是否可以完成.

在这种阻塞调用期间发生的事情是计算停止,直到释放被阻止的缓冲区.这通常会导致计算资源的浪费,因为Send/Recv通常将数据从一个存储器位置复制到另一个存储器位置,而cpu中的寄存器保持空闲.

非阻塞通信:对于非阻塞通信,该应用程序用于发送创建用于通信的请求和/或接收和回来的手柄,然后终止.这就是保证流程执行所需的全部内容.即MPI库被通知必须执行操作.

对于发送方,这允许与通信重叠计算.

对于接收器侧,这允许重叠通信开销的一部分,即将消息直接复制到应用中的接收侧的地址空间中.

  • 希望我两个月前读过这篇文章。非常感谢您的解释 - 对我帮助很大。 (2认同)

pea*_*man 11

在使用阻塞通信时,您必须关心发送和接收呼叫,例如查看此代码

 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);
 }
Run Code Online (Sandbox Code Playgroud)

在这种情况下会发生什么?

  1. 进程0将x发送到进程1并阻塞,直到进程1收到x.
  2. 进程1将y发送到进程0并阻塞,直到进程0接收到y,但是
  3. 过程0被阻止,使得进程1阻塞无穷大,直到两个进程被终止.

  • 我曾经这么认为.但是当我在计算机上使用MPI_Send时,我发现里面的问题可能会更复杂.上面的代码可以工作,因为它可以将消息移动到缓冲区.只有'MPI_Ssend`严格*阻塞*,因为它返回直到目标收到消息.以下链接说明了不同的供应商选择不同的实现.http://www.mcs.anl.gov/research/projects/mpi/sendmode.html (7认同)

Vic*_*out 6

接受的答案和另一个很长的答案都提到计算和通信的重叠是一种优势。这 1. 不是主要动机,2. 很难实现。非阻塞通信的主要优点(也是最初的动机)是,您可以表达复杂的通信模式,而不会陷入死锁,并且进程不会不必要地序列化自身。

示例: 死锁:每个人都进行接收,然后每个人都进行发送,例如沿着环。这会挂起。

序列化:沿着线性顺序,除了最后一个之外的每个人都向右发送,然后除了第一个之外的每个人都从左侧接收。这将使所有进程顺序执行而不是并行执行。


Art*_*nce 5

这很容易。

非阻塞意味着单个进程的计算和传输数据可以同时发生。

而阻塞则意味着,嘿伙计,你必须确保你已经完成了数据传输,然后再回来完成下一个命令,这意味着如果有一个传输后面有一个计算,那么计算必须在传输成功之后。