在 MPI(Message Passing Interface) 中,多进程同步是一个关键问题,尤其是在并行计算中确保数据一致性、避免竞态条件和协调进程执行顺序时。以下是 MPI 多进程同步 的几种主要方法及其应用场景。
1. MPI 同步机制概述
MPI 提供了多种同步方式,主要分为:
显式同步(如 MPI_Barrier)
隐式同步(如 MPI_Send/MPI_Recv 的阻塞通信)
集合通信(如 MPI_Bcast, MPI_Reduce)
2. 常用 MPI 同步方法
(1) MPI_Barrier(全局同步)
作用:让所有进程在 Barrier 处等待,直到所有进程都到达该点才继续执行。
语法:
MPI_Barrier(MPI_Comm comm);
适用场景:
确保所有进程完成某个阶段的计算后再进入下一阶段。
调试时检查进程是否同步执行。
示例:hello.c
#include <mpi.h>
#include <stdio.h>
int main(int argc, char **argv) {
MPI_Init(&argc, &argv);
int rank;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
printf("Rank %d: Before Barrier\n", rank);
MPI_Barrier(MPI_COMM_WORLD); // 所有进程在此同步
printf("Rank %d: After Barrier\n", rank);
MPI_Finalize();
return 0;
}
编译运行:
$ mpiCC hello.c -o hello
$ mpiexec -np 7 ./hello
输出:

(2) 阻塞通信(MPI_Send/MPI_Recv)
作用:发送和接收操作本身具有同步性,MPI_Send 会等待接收方 MPI_Recv 就绪(取决于 MPI 实现)。
适用场景:点对点通信时隐式同步。确保数据发送和接收的顺序。
示例:
int data;
if (rank == 0) {
data = 100;
MPI_Send(&data, 1, MPI_INT, 1, 0, MPI_COMM_WORLD); // 发送给 Rank 1
} else if (rank == 1) {
MPI_Recv(&data, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
printf("Rank 1 received: %d\n", data);
}
(3) 集合通信(Collective Communication)
MPI 的集合通信操作(如 MPI_Bcast, MPI_Reduce)隐式包含同步,所有进程必须参与调用。
MPI_Bcast(广播)
int data;
if (rank == 0) {
data = 123;
}
MPI_Bcast(&data, 1, MPI_INT, 0, MPI_COMM_WORLD); // Rank 0 广播数据
printf("Rank %d received: %d\n", rank, data);
MPI_Reduce(归约)
int local_val = rank + 1;
int global_sum;
MPI_Reduce(&local_val, &global_sum, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);
if (rank == 0) {
printf("Global sum: %d\n", global_sum);
}
3. 高级同步机制
(1) MPI_Win(窗口同步,用于单边通信 RMA)
适用于 远程内存访问(RMA),如 MPI_Put/MPI_Get:
MPI_Win win;
int *shared_data;
MPI_Win_allocate(sizeof(int), sizeof(int), MPI_INFO_NULL, MPI_COMM_WORLD, &shared_data, &win);
MPI_Win_fence(0, win); // 同步所有进程的窗口
if (rank == 0) {
*shared_data = 42;
}
MPI_Win_fence(0, win); // 确保写入完成
if (rank == 1) {
printf("Rank 1 sees: %d\n", *shared_data);
}
MPI_Win_free(&win);
(2) MPI_Request(非阻塞通信 + 显式同步)
非阻塞通信(MPI_Isend/MPI_Irecv)需要 MPI_Wait 或 MPI_Test 来同步:
MPI_Request request;
int buffer;
if (rank == 0) {
buffer = 99;
MPI_Isend(&buffer, 1, MPI_INT, 1, 0, MPI_COMM_WORLD, &request);
MPI_Wait(&request, MPI_STATUS_IGNORE); // 等待发送完成
} else if (rank == 1) {
MPI_Irecv(&buffer, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, &request);
MPI_Wait(&request, MPI_STATUS_IGNORE); // 等待接收完成
printf("Rank 1 received: %d\n", buffer);
}
4. 同步问题与最佳实践
(1) 避免死锁
错误示例(两个进程互相 MPI_Send):
if (rank == 0) {
MPI_Send(buf, size, MPI_INT, 1, tag, MPI_COMM_WORLD);
MPI_Recv(buf, size, MPI_INT, 1, tag, MPI_COMM_WORLD, &status);
} else if (rank == 1) {
MPI_Send(buf, size, MPI_INT, 0, tag, MPI_COMM_WORLD);
MPI_Recv(buf, size, MPI_INT, 0, tag, MPI_COMM_WORLD, &status);
}
问题:两个进程都在 MPI_Send 阻塞,无法继续执行 → 死锁。
解决方案:
使用 MPI_Sendrecv(发送和接收结合):
MPI_Sendrecv(sendbuf, sendcount, sendtype, dest, sendtag,
recvbuf, recvcount, recvtype, source, recvtag,
comm, &status);
或使用非阻塞通信(MPI_Isend/MPI_Irecv)。
(2) 减少 MPI_Barrier 的使用
MPI_Barrier 是全局同步,可能成为性能瓶颈。尽量用 集合通信 或 点对点通信 代替。
5. 小结一下
有点乱,总结一下,
同步方法 适用场景 特点
MPI_Barrier 全局同步 简单,但可能影响性能
MPI_Send/MPI_Recv 点对点通信隐式同步 适用于小规模数据交换
MPI_Bcast/MPI_Reduce 集合通信 高效,适用于规约操作
MPI_Win(RMA) 远程内存访问 适用于共享内存式编程
MPI_Request 非阻塞通信 + 显式同步 提高并行效率
根据具体需求选择合适的同步机制,避免不必要的全局同步,以提高并行程序的性能!

2435

被折叠的 条评论
为什么被折叠?



