为何该MPI通信示例未触发死锁却能正常运行?
为什么我的MPI程序没有出现预期的死锁?
你的疑惑核心在于对MPI阻塞发送的理解偏差——MPI_Send的阻塞不是必须等待接收方调用MPI_Recv,而是等待数据被安全处理。下面具体解释:
1. MPI_Send的实际行为逻辑
MPI标准并没有强制规定阻塞发送的具体实现,不同MPI库(比如OpenMPI、MPICH)会根据消息大小自动选择处理策略:
- 缓冲式发送:当消息大小小于MPI内置缓冲区阈值时,
MPI_Send会直接把数据拷贝到系统/MPI提供的缓冲区,然后立即返回,完全不需要等待接收方的MPI_Recv操作。 - 同步式发送:当消息超过缓冲阈值时,
MPI_Send才会阻塞,直到接收方调用了匹配的MPI_Recv,并且数据完成传输。
你的程序里只发送了1个int类型的数据,远小于绝大多数MPI实现的默认缓冲阈值(比如OpenMPI默认是64KB),所以进程0的两次MPI_Send都直接把数据丢进缓冲区就返回了,根本不会阻塞。这时候进程1不管先收哪个标签的消息,都能从缓冲区里拿到对应的数据,自然不会触发死锁。
2. 如何触发预期的死锁?
要让程序陷入死锁,你需要让MPI_Send必须等待接收方的Recv:
- 增大消息大小:把
#define N 1改成#define N 100000(具体数值可根据你的MPI实现缓冲大小调整,确保超过阈值),这时进程0的第一个MPI_Send会因为无法缓冲而阻塞,等待进程1接收标签98的消息,但进程1一直在等标签99的消息,双方就会互相卡住。 - 禁用MPI缓冲:运行程序时加上MPI库的特定参数,比如OpenMPI可以用
mpirun -n 2 --mca btl_sm_use_heap 0 ./debug.out,强制禁用共享内存的缓冲机制,这样即使小消息也会触发同步式发送,导致死锁。
你的程序代码
#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.
内容的提问来源于stack exchange,提问作者Yujia Mountain Boar King
相关产品推荐
相关产品推荐

