mpi:阻塞与非阻塞

时间:2012-04-04 18:39:45

标签: parallel-processing mpi cluster-computing

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

谢谢!

4 个答案:

答案 0 :(得分:77)

使用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的发送模式的快速概述here

答案 1 :(得分:30)

这篇文章虽然有点陈旧,但我认为接受了答案。声明"这些功能在通信结束之前不会返回"有点误导,因为阻止通信并不保证发送和接收操作的任何握手。

第一个需要知道,发送有四种模式的通信:标准,缓冲,同步就绪,每个都可以 阻止 非阻止

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

在继续进行之前,还必须明确我明确提到哪一个是 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)

<强> 2。缓冲模式

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

缓冲区发送的语法:

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。就绪模式

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

ready send语法:

int MPI_Rsend(const void *buf, int count, MPI_Datatype datatype, int dest, 
              int tag, MPI_Comm comm)

经历了所有4次阻塞发送后,它们看起来可能与主要不同,但根据实现,一种模式的语义可能与另一种模式类似。

例如,MPI_Send通常是一种阻塞模式,但根据实现,如果消息大小不是太大,MPI_Send会将传出消息从发送缓冲区复制到系统缓冲区(&#39;现代主要是这种情况)系统)并立即返回。让我们看一下下面的例子:

//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和等级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 :(得分:10)

在使用阻止通信时,您必须关注发送和接收呼叫,例如 看看这段代码

 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);
 }

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

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

答案 3 :(得分:4)

很容易。

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

虽然Blocking意味着,嘿伙计,你必须确保你已经完成了传输数据然后返回完成下一个命令,这意味着如果有一个转移后跟一个计算,计算必须在成功之后转移。