您的位置:首页 > 其它

MPI非阻塞通信使用、性能分析与实现原理

2016-08-03 16:40 411 查看
非阻塞通信:

异步通信通常是使MPI应用程序实现高性能计算的关键,使用异步通信具有如下优势:

1)函数是非阻塞的,这使得进程在与另一个进程通信的同时继续参与计算;

2)如果应用适当,可以绕过MPI的内部buffers,极大地提高程序的通信带宽;

最常见的基本的非阻塞调用就是MPI_Isend和MPI_Irecv

MPI_Isend的使用

与同步版本的MPI_Send不同,MPI_Isend是非阻塞的。如果进程使用这一函数发送数据,函数执行完后立即返回,不用等待数据发送完毕。不过,需要注意的是,非阻塞调用在数据发送结束之前返回。所以函数MPI_Isend发送数据使用的缓冲区必须要等到数据发送结束才能够使用。例如,如下的代码是不安全的:
int i = 123;
MPI_Request myRequest;
MPI_Isend(&i, 1, MPI_INT, 1, MY_LITTLE_TAG, MPI_COMM_WORLD, &myRequest);
i = 234;


在上面的代码中,注意到变量i的地址传递给MPI_Isend函数作为数据发送的缓冲区地址,此处只发送一个整型数据。现在注意到在函数MPI_Isend调用之后,立刻修改变量i。这一做法实际上是不安全的,因为当我们修改变量i的值以后,数据传输没有开始或没有完成,MPI程序有可能会读取它,此时发送的数据就由123变成了234。这是一种竞争条件,很难调试出来。

避免竞争条件:
如果进程使用函数MPI_Isend和数据缓冲区buffer发送数据,并且在MPI_Isend调用后的某一执行点,需要继续使用buffer,那么就有必要等待异步发送结束后。MPI提供给我们一个函数用来检测异步发送是否完成:MPI_Wait

int i = 123;
MPI_Request myRequest;
MPI_Isend(&i, 1, MPI_INT, 1, MY_LITTLE_TAG, MPI_COMM_WORLD, &myRequest);

MPI_Status myStatus;
MPI_Wait(&myRequest, &myStatus);
i = 234;


在上面的代码中,我们看到,进程在通信的过程中,仍然可以同时做一些其他的计算操作。非阻塞调用可以实现计算与通信的并行,这极大的提高了并行程序的效率。

接收端:

MPI_Irecv函数负责接收数据,与MPI_Isend相对应。调用以后,MPI_Irecv会立即返回,这允许进程一边接收数据一边参与与接收缓冲区无关的计算。然而,当接收进程实际使用所接收的数据的时候,必须调用MPI_Wait函数以便确保接收到的数据是有效的。


异步接收的性能:


由于异步接收和发送的特性,发送进程可以在接收进程调用MPI_Irecv之前调用MPI_Isend发送数据。在这种情况下,MPI实现会暂时将数据存储在接收进程所在主机的内部缓冲区中。但是,如果在发送进程调用MPI_Isend发送数据前,接收进程已经调用MPI_Irecv准备好接收数据,那么MPI实现就只是简单的填充程序提供的缓冲区(发送,接收缓冲区)。而不用使用MPI提供的内部缓冲区。这可以有效的去掉发生在接收进程端的内存拷贝操作,对于大数据量的处理来说,这会极大的提高性能。

因此,当发送大量数据时,最好在发送进程调用MPI_Isend之前,接收进程先调用MPI_Irecv。很多时候,这很难实现,但还是可以的,这可以帮你的程序挤出一些额外的性能。

计算与通信重叠的关键:

对于如下的数据分布,相邻结点互相之间需要对方的边界数据,为了使用通信与计算重叠,提高程序效率,可以先计算边界数据,然后使用mpi_isend异步发送,同时计算中心数据,这样可以达到将通信隐藏的目的。如果直接使用mpi_send的话会出现阻塞,双方都在等待对方接受完毕以继续,循环依赖出现死锁,需要针对死锁做进一步处理。



非阻塞通信实现原理:

根据我自己的理解其实也没有那么高深,阻塞通信出现死锁的原因并不仅仅是相邻结点同时互相发数据,也是因为阻塞通信要求确认对方已经接收完毕才会返回,此死锁主要是出现在逻辑上。非阻塞其实就是不等待对方接受,将发送数据的任务交给操作系统由系统调用(send/recv/read/write之类的)去完成,这样仅仅从逻辑上就可以避免死锁。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息