阻塞通信与非阻塞通信区别
阻塞通信:在调用返回结果之前,当前进程会一直阻塞,即进程悬挂。阻塞式发送可以理解为,只有进程在确定消息完全发出去(发送到缓冲区)后才会继续执行下一条指令,在此之前会一直等待;阻塞式接受也可以理解为,当前进程在未接受到完整的消息时,会一直等待,直到接收结束才会继续执行。在阻塞通信还没有结束的时候,处理器只能等待,这样就浪费了处理机的计算资源。
非阻塞式通信:对于非阻塞通信 ,不必等到通信操作完全完成便可以返回 ,该通信操作可以交给特定的,通信硬件去完成,在该通信硬件完成该通信操作的同时,处理机可以同时进行计算操作,这样便实现了计算与通信的重叠。
阻塞通信的四种模式:
通信模式 | 发送 | 接收 |
---|---|---|
标准通信模式 | MPI_SEND | MPI_RECV |
缓存通信模式 | MPI_BSEND | |
同步通信模式 | MPI_SSEND | |
就绪通信模式 | MPI_RSEND |
标准通信模式:
MPI_SEND(buf,count,datatype,dest,tag,comm)
IN buf 发送缓冲区的起始地址(可选类型)
IN count 将发送的数据的个数(非负整数) 以数据类型为单位,非字节
IN datatype 发送数据的数据类型(句柄)
IN dest 目的进程标识号(整型)
IN tag 消息标志(整型) 可把某进程向同一目的进程发送的多条消息区别开来
IN comm 通信域(句柄)
int MPI_Send(void* buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)
消息组装完毕后,发送进程可以缓冲消息,也可以阻塞,具体进行哪一种行为是由MPI自身决定的,并行程序员不可控制。
如果MPI决定缓存将要发出的数据,发送操作不管接收操作是否执行,都可以进行,MPI系统会把消息(包括数据和信封)放置在自己的内部存储器里(不同于发送和接收缓冲区的临时系统缓冲区),并返回MPI_Send的调用,不必考虑接受操作是否已经收到数据。但缓存数据会延长数据通信的时间,而且缓冲区也并不是总可以得到的。
另一种方式不必缓存数据,即阻塞,只有当相应的接收调用被执行后,并且发送数据完全到达接收缓冲区后,发送操作才算完成,才会正确返回。
所以当MPI_Send函数返回时,实际并不知道消息是否已经发出去,只知道发送消息原占用的发送缓冲区可以被程序再次使用。MPI_Send函数可能开始被调用,无论相对应的接收函数是否被调用。发送函数可能在对应的接收函数被调用前返回。即标准模式的发送是非本地的:发送操作的成功完成可能会依赖于对应的接收函数的的调用。
缓冲通信模式:
MPI_BSEND(buf, count, datatype, dest, tag, comm)
IN buf 发送缓冲区的起始地址(可选数据类型)
IN count 发送数据的个数(整型)
IN datatype 发送数据的数据类型(句柄)
IN dest 目标进程标识号(整型)
IN tag 消息标志(整型)
IN comm 通信域(句柄)
int MPI_Bsend(void* buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)
MPI_BSEND的各个参数的含义和MPI_SEND的完全相同,不同之处是其使用用户自己提供的缓冲区,缓存通信模式不管接收操作是否启动,发送操作都可以执行,但是在发送消息之前必须有缓冲区可用。在这种模式下由用户直接对通信缓冲区进行申请、使用和释放,缓存模式下对通信缓冲区的合理与正确使用是由程序设计人员自己保证的。这一操作是本地的。
常用调用:
MPI_BUFFER_ATTACH( buffer, size)
IN buffer 初始缓存地址(可选数据类型)
IN size 按字节计数的缓存跨度(整型)
int MPI_Buffer_attach( void* buffer, int size)
MPI_BUFFER_ATTACH将大小为size的缓冲区递交给MPI 这样该缓冲区就可以作为缓存发送时的缓存来使用。
MPI_BUFFER_DETACH( buffer, size)
OUT buffer 缓冲区初始地址(可选数据类型)
OUT size 以字节为单位的缓冲区大小(整型)
int MPI_Buffer_detach( void** buffer, int* size)
MPI_BUFFER_DETACH将提交的大小为size的缓冲区buffer收回,该调用是阻塞调用,它一直等到使用该缓存的消息发送完成后才返回。
同步通信模式:
MPI_SSEND(buf, count, datatype, dest, tag, comm)
IN buf 发送缓冲区的初始地址(可选数据类型)
IN count 发送数据的个数(整型)
IN datatype 发送数据的数据类型(句柄)
IN dest 目标进程号(整型)
IN tag 消息标识(整型)
IN comm 通信域(句柄)
int MPI_Ssend(void* buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)
同步通信模式的开始不依赖于接收进程相应的接收操作是否已经启动,但是同步发送却必须等到接收函数被调用,并且开始接收由同步发送函数发送的消息后发送函数才成功返回。因此,同步发送返回后意味着发送缓冲区中的数据已经全部被系统缓冲区缓存,并且已经开始发送。这样当同步发送返回后,发送缓冲区可以被释放或重新使用。在这种模式下的发送操作是非本地。
就绪通信模式:
MPI_RSEND(buf, count, datatype, dest, tag, comm)
IN buf 发送缓冲区的初始地址(可选数据类型)
IN count 将发送数据的个数(整型)
IN datatype 发送数据的数据类型(句柄)
IN dest 目标进程标识(整型)
IN tag 消息标识(整型)
IN comm 通信域(句柄)
int MPI_Rsend(void* buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)
在就绪通信模式只有当接收进程的接收操作已经启动时,才可以在发送进程启动发送操作 ,否则,当发送操作启动而相应的接收还没有启动时,发送操作将出错。
消息接收:
MPI_RECV(buf,count,datatype,source,tag,comm,status)
OUT buf 接收缓冲区的起始地址(可选数据类型)
IN count 最多可接收的数据的个数(整型)
IN datatype 接收数据的数据类型(句柄)
IN source 接收数据的来源即发送数据的进程的进程标识号(整型)
IN tag 消息标识 与相应的发送操作的表示相匹配相同(整型)
IN comm 本进程和发送进程所在的通信域(句柄)
OUT status 返回状态 (状态类型)
int MPI_Recv(void* buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status *status)
仅有一种接收操作模式,但是它可以与任意一种发送模式相匹配。
与MPI_Send不同,MPI_Recv函数总是阻塞的,直到接收到一条匹配消息。当MPI_Recv函数调用返回时,接收缓冲区中一定已存储了一条消息,可以被程序引用。
MPI消息的“顺序性”
MPI要求消息是不可超越的(nonovertaking)。即如果q号进程发送了两条消息给r号进程,那么q进程发送的第一条消息必须在第二条消息之前可用。但是,如果消息是来自不同进程的,消息的到达顺序是没有限制的。即如果q号进程和t号进程都向r号进程发送了消息,即使q号进程在t号进程发送消息之前就将自己的消息发送出去了,也不要求q号进程的消息在t号进程的消息之前一定能被r号进程所访问。这本质上是因为 MPI不能对网络的性能有强制性要求。例如,如果q号进程在火星上的某台机器上运行,而r号进程和t号进程都在旧金山的同一台机器上运行,并且q号进程只是在t号进程发送消息之前的1纳秒发送了消息,那么要求q号进程的消息在t号进程之前到达,是不合理的。