当我们使用mpi_send / receive函数时到底发生了什么?

时间:2012-06-01 12:55:15

标签: c++ mpi

当我们使用mpi_send/receive函数时会发生什么?我的意思是这个通信是通过我们希望发送和接收的变量的值或地址来完成的(例如,进程0想要发送变量“a”到进程1.进程0究竟发送变量“a”或地址的值“a”)。当我们使用派生数据类型进行通信时会发生什么?

3 个答案:

答案 0 :(得分:21)

幕后发生了相当多的魔力。


首先,意外消息队列。当发送方在接收方呼叫MPI_Send之前调用MPI_Recv时,MPI不知道消息在接收方内存中的位置。此时可能会发生两件事。如果消息很短,则将其复制到接收器的临时缓冲区。当接收方呼叫MPI_Recv时,它首先检查匹配的消息是否已经到达,如果已经到达,则将数据复制到最终目的地。如果不是,则将有关目标缓冲区的信息存储在队列中,以便在消息到达时匹配MPI_Recv。可以使用MPI_Probe检查意外队列。

如果邮件超过某个阈值,则复制邮件会花费太长时间。相反,发送方和接收方使用某种集合协议进行握手,以确保目标在发送之前已准备好接收消息。这对于像InfiniBand这样的高速网络尤其重要。


如果通信排名位于同一台计算机上,通常数据传输通过共享内存进行。因为MPI排名是独立进程,所以它们无法访问彼此的内存。相反,同一节点上的MPI进程设置共享内存区域并使用它来传输消息。因此,发送消息涉及将数据复制两次:发送方将其复制到共享缓冲区中,接收方将其复制到自己的地址空间中。这有例外。如果MPI库配置为使用类似KNEM的内核模块,则可以将消息直接复制到OS内核中的目标。但是,这样的副本会导致系统调用的惩罚。通过内核进行复制通常仅适用于大型消息。像Catamount这样的专业HPC操作系统可以改变这些规则。


集体操作可以在发送/接收方面实施,也可以实现完全独立的优化实施。通常具有用于集体操作的若干算法的实现。 MPI库在运行时决定使用哪一个以获得最佳性能,具体取决于消息和通信器的大小。


一个好的MPI实现将非常努力地传输派生的数据类型而不创建额外的副本。它将确定数据类型中哪些内存区域是连续的并单独复制它们。但是,在某些情况下,MPI将回退到在幕后使用MPI_Pack使消息连续,然后传输并解压缩。

答案 1 :(得分:6)

就应用系统程序员而言,需要关注这些操作发送和接收数据,而不是数据地址。 MPI进程不共享地址空间,因此进程0上的地址对进程1上的操作没有意义 - 如果进程1希望数据位于进程0的地址,则它必须从进程0获取它。我不认为与MPI-2一起进行的单面通信对这种情况产生了重大影响。

MPI库开发人员的观点可能会有所不同,并且肯定会依赖于实现。例如,如果您在共享内存计算机上使用编写良好的MPI库,那么可能只是通过向系统周围的地址位置发送指针来实现消息传递。但这是一个极端的案例,现在看来并不多见。

答案 2 :(得分:1)

mpi_send要求您将地址提供给包含要发送数据的内存。它只会在为您保存时重新使用该内存(这可以通过非阻塞通信来避免)。

同样,mpi_recv要求您提供足够的内存地址,以便可以复制要接收的数据。只有当数据已经被接收到该缓冲区时它才会返回。

MPI如何做到这一点,是另一回事,你不必担心写一个有效的MPI程序(但可能写一个有效的程序)。