提问人:Yujia Mountain Boar King 提问时间:4/21/2023 更新时间:4/21/2023 访问量:36
为什么这样的 MPI 通信示例可以运行?
Why such an MPI communication example can run?
问:
这是我的 MPI 程序。在此程序的第 29 行和第 30 行,我分别发送了两条带有 98 和 99 标签的消息,但在第 33 行和第 34 行中,我希望另一个程序首先接收带有 99 标签的消息,然后再接收带有 98 标签的程序。在我的理解中,因为 和 是阻塞功能,这些四行程序会陷入相互等待的境地,但这在实际操作中不会发生。在调试期间,该函数也不会被阻止。为什么?MPI_Send()
MPI_Recv()
MPI_Send()
#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define N 1
int main(int argc, char **argv) {
#ifdef DEBUG
int i = 0;
while (0 == i) {
sleep(1);
}
#endif
int myrank, dest;
int my_int[N], get_int[N];
MPI_Status status;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &myrank);
my_int[0] = myrank;
get_int[0] = myrank + 1;
dest = (myrank == 0) ? 1 : 0;
if (myrank == 0) {
MPI_Send(my_int, N, MPI_INT, dest, 98, MPI_COMM_WORLD);
MPI_Send(get_int, N, MPI_INT, dest, 99, MPI_COMM_WORLD);
} else {
printf("myrank: %d my_int = %d get_int = %d.\n", myrank, my_int[0],
get_int[0]);
MPI_Recv(get_int, N, MPI_INT, 0, 99, MPI_COMM_WORLD, &status);
MPI_Recv(my_int, N, MPI_INT, 0, 98, MPI_COMM_WORLD, &status);
printf("myrank: %d my_int = %d get_int = %d.\n", myrank, my_int[0],
get_int[0]);
}
MPI_Finalize();
return 0;
}
mpirun -n 2 ./debug.out
myrank: 1 my_int = 1 get_int = 2.
myrank: 1 my_int = 0 get_int = 1.
我认为这个程序打开了两个将进入僵局状态的进程,但它们没有。
答:
在 MPI 中,有三种不同的协议用于发送消息。根据实际实现,这些协议还有其他子类型和特征。
一般来说,它们是简短的、渴望的和会合的协议。
简而言之,协议中数据与信封(MPI 消息由信封和数据组成。信封由信息组成 关于标签、传播者等)到接收者并存储在 接收器处的预分配缓冲区。
在 eager 协议中,发送消息时假设 接收进程分配了缓冲区,目标可以 存储消息。这需要在接收器处进行缓冲。这 表示发送方将作为消息完成 MPI_Send() 调用 已发送,但尚未在接收方接收。
在会合协议中,消息不会发送,直到 目的地和发件人已协商(匹配的接收已发布 然而)。
正如 @Gilles Gouaillardet 在评论中提到的,在您的情况下,可能会使用 eager 协议,并且不会(假设)出现僵局。如果以迭代方式增加消息大小,您将能够看到进程将死锁。在某些 MPI 实现中,您可以自己设置这个预先限制并使用它(例如:openMPI 中的参数)。btl_vader_eager_limit
换句话说,MPI_Send可能会阻止也可能不会阻止。这是特定于实现的。它将阻止,直到发送方可以重用发送方缓冲区。当缓冲区发送到较低的通信层时,某些实现将返回给调用方。当另一端有匹配的 MPI_Recv() 时,其他一些将返回给调用方。因此,此程序是否会死锁取决于您的 MPI 实现,以及您避免死锁的责任:)。请参阅此 SO 线程。
评论
MPI_Ssend()