8

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

4

3 回答 3

23

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


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

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


如果通信队列在同一台机器上,通常数据传输是通过共享内存进行的。因为 MPI rank 是独立的进程,所以它们不能访问彼此的内存。相反,同一节点上的 MPI 进程设置了一个共享内存区域并使用它来传输消息。因此,发送消息涉及复制数据两次:发送方将其复制到共享缓冲区中,接收方将其复制到自己的地址空间中。这有一个例外。如果 MPI 库配置为使用KNEM等内核模块,消息可以直接复制到操作系统内核中的目的地。然而,这样的拷贝会招致系统调用的惩罚。通过内核进行复制通常只对大消息是值得的。Catamount 等专门的 HPC 操作系统可以改变这些规则。


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


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

于 2012-06-01T15:59:50.380 回答
6

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

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

于 2012-06-01T13:03:04.477 回答
1

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

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

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

于 2012-06-01T13:06:37.973 回答