MPI_Send未阻塞执行?自定义O(2log₂n)时间Barrier函数的困惑
MPI Barrier实现问题:MPI_Send阻塞机制的误解?
我写了一个MPI程序,目标是实现一个Barrier函数——要求所有进程进入该函数后才能继续执行,且时间复杂度约为2log₂(n)。但打印进程进入Barrier的时间点以及收发消息的时间点后,实际时序和预期不符。是不是我误解了MPI_Send的工作机制?我原本以为它会一直阻塞,直到对应进程执行了匹配的Recv操作。
程序代码
#include<iostream> #include<mpi.h> #include<time.h> #include<windows.h> #define MESSAGE_TAG 999 void barrier(); using namespace std; int main(int argc, char* argv[]) { MPI_Init(NULL, NULL); int my_rank; int world_size; MPI_Comm_size(MPI_COMM_WORLD, &world_size); MPI_Comm_rank(MPI_COMM_WORLD, &my_rank); barrier(); MPI_Finalize(); return 0; } void barrier() { int my_rank; int world_size; MPI_Status status; char message[6] = ""; clock_t startTime, endTime; double duration; startTime = clock(); MPI_Comm_size(MPI_COMM_WORLD, &world_size); MPI_Comm_rank(MPI_COMM_WORLD, &my_rank); if (my_rank == 2) { Sleep(5000); } endTime = clock(); duration = ((double)endTime - startTime) / CLOCKS_PER_SEC; printf("Beginning of barrier for process %d at time %f\n", my_rank, duration); if (my_rank == 0) { message[0] = 'r'; message[1] = 'e'; message[2] = 'a'; message[3] = 'd'; message[4] = 'y'; message[5] = '\0'; } int stepNumber = 0; int totalStepsNeeded = (int) (2*(floor(log2(world_size))-1)); int closestPower2 = (int) pow(2, ((totalStepsNeeded/2)+1)); int excessProcs = world_size - closestPower2; if (my_rank < closestPower2) { while (stepNumber < totalStepsNeeded) { if (my_rank % 2 == 0) { int reciever = (stepNumber * 2) + 1 + my_rank; if (reciever > closestPower2) reciever = reciever - closestPower2; MPI_Send(message, 10, MPI_CHAR, reciever, MESSAGE_TAG, MPI_COMM_WORLD); endTime = clock(); duration = ((double)endTime - startTime) / CLOCKS_PER_SEC; printf("Process %d sent to process %d at time %f\n", my_rank, reciever, duration); } else { int sender = my_rank - (stepNumber * 2) - 1; if (sender < 0) sender = closestPower2 + sender; MPI_Recv(message, 10, MPI_CHAR, sender, MESSAGE_TAG, MPI_COMM_WORLD, &status); endTime = clock(); duration = ((double)endTime - startTime) / CLOCKS_PER_SEC; printf("Process %d recieved from process %d at time %f\n", my_rank, sender, duration); } stepNumber++; } } if (my_rank < excessProcs) { MPI_Send(message, 10, MPI_CHAR, my_rank + closestPower2, MESSAGE_TAG, MPI_COMM_WORLD); } else if (my_rank >= closestPower2) { MPI_Recv(message, 10, MPI_CHAR, my_rank - closestPower2, MESSAGE_TAG, MPI_COMM_WORLD, &status); } endTime = clock(); duration = ((double)endTime - startTime) / CLOCKS_PER_SEC; printf("End of barrier for process %d at time %f", my_rank, duration); }
输出结果
Beginning of barrier for process 4 at time 0.000000 Process 4 sent to process 5 at time 0.000000 Process 4 sent to process 7 at time 0.000000 Process 4 sent to process 1 at time 0.000000 Process 4 sent to process 3 at time 0.000000 End of barrier for process 4 at time 0.000000 Beginning of barrier for process 5 at time 0.000000 Process 5 recieved from process 4 at time 0.001000 Process 5 recieved from process 2 at time 5.005000 Process 5 recieved from process 0 at time 5.005000 Process 5 recieved from process 6 at time 5.005000 End of barrier for process 5 at time 5.005000 Beginning of barrier for process 1 at time 0.000000 Process 1 recieved from process 0 at time 0.001000 Process 1 recieved from process 6 at time 0.001000 Process 1 recieved from process 4 at time 0.001000 Process 1 recieved from process 2 at time 5.006000 End of barrier for process 1 at time 5.006000 Beginning of barrier for process 6 at time 0.000000 Process 6 sent to process 7 at time 0.000000 Process 6 sent to process 1 at time 0.000000 Process 6 sent to process 3 at time 0.000000 Process 6 sent to process 5 at time 0.000000 End of barrier for process 6 at time 0.000000 Beginning of barrier for process 2 at time 5.003000 Process 2 sent to process 3 at time 5.004000 Process 2 sent to process 5 at time 5.004000 Process 2 sent to process 7 at time 5.005000 Process 2 sent to process 1 at time 5.005000 End of barrier for process 2 at time 5.005000 Beginning of barrier for process 0 at time 0.000000 Process 0 sent to process 1 at time 0.001000 Process 0 sent to process 3 at time 0.001000 Process 0 sent to process 5 at time 0.001000 Process 0 sent to process 7 at time 0.001000 End of barrier for process 0 at time 0.001000 Beginning of barrier for process 7 at time 0.000000 Process 7 recieved from process 6 at time 0.001000 Process 7 recieved from process 4 at time 0.001000 Process 7 recieved from process 2 at time 5.005000 Process 7 recieved from process 0 at time 5.005000 End of barrier for process 7 at time 5.005000 Beginning of barrier for process 3 at time 0.000000 Process 3 recieved from process 2 at time 5.004000 Process 3 recieved from process 0 at time 5.004000 Process 3 recieved from process 6 at time 5.004000 Process 3 recieved from process 4 at time 5.004000 End of barrier for process 3 at time 5.004000
问题分析与解决方案
1. MPI_Send的阻塞机制误解
MPI_Send并非一定会阻塞到匹配的Recv执行,它的行为由MPI实现和消息大小决定:
- 小消息(如你这里的10字节)会被MPI系统缓存,发送方拷贝消息到缓冲区后立即返回,无需等待接收方调用Recv。只有当消息超过缓冲区大小时,才会阻塞到接收方开始接收。
- 你看到进程4在进程3调用Recv前完成Send,就是这个原因,导致进程4直接走完Barrier逻辑,违背了同步要求。
2. Barrier实现的逻辑缺陷
除了对MPI_Send的误解,你的算法本身也不满足Barrier的同步要求:
- 单向消息传递:发送方发送后直接继续执行,没有等待接收方的确认,无法保证双方同步。
- 非2幂次进程的处理逻辑孤立:excessProcs部分的收发没有和主同步流程结合,无法保证这部分进程与其他进程同步。
- 时间基准错误:
clock()获取的是进程自身CPU时间,不同进程的时间起点不一致,时序打印不准确,建议改用MPI_Wtime()获取全局墙钟时间。
3. 修复建议
- 使用同步Send保证阻塞:将
MPI_Send替换为MPI_Ssend,它会强制阻塞直到接收方调用了匹配的Recv,确保消息传递的同步性。 - 实现标准树形Barrier算法:
- 上升阶段:进程向父节点发送就绪信号,父节点等待所有子节点信号后再向上传递。
- 下降阶段:根节点向子节点发送释放信号,子节点再向下传递给子节点。
该算法时间复杂度为O(log₂n),能严格保证所有进程同步后才继续执行。
- 统一时间基准:用
MPI_Wtime()替代clock(),获取全局同步的时间戳,让时序打印更准确。
内容的提问来源于stack exchange,提问作者Ethan Harness
相关产品推荐
相关产品推荐

