OpenMPI (分布式内存并行) 和 OpenMP (共享内存并行) 的混合编程可以充分发挥现代多核集群的计算能力。这种混合模式通常被称为 MPI+OpenMP 编程模型。
OpenMPI 负责节点间通信 (跨计算机/进程)
OpenMP 负责节点内多线程并行 (单计算机内多核)
每个 MPI 进程创建一组 OpenMP 线程
集群节点1:
MPI进程1 (主线程)
├─ OpenMP线程1
├─ OpenMP线程2
└─ OpenMP线程N
集群节点2:
MPI进程2 (主线程)
├─ OpenMP线程1
├─ OpenMP线程2
└─ OpenMP线程N
#include
#include
#include
int main(int argc, char *argv[]) {
// MPI初始化
MPI_Init(&argc, &argv);
int mpi_rank, mpi_size;
MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank);
MPI_Comm_size(MPI_COMM_WORLD, &mpi_size);
// OpenMP并行区域
#pragma omp parallel
{
int omp_thread = omp_get_thread_num();
int omp_num_threads = omp_get_num_threads();
printf("MPI进程 %d/%d, OpenMP线程 %d/%d: Hello!\n",
mpi_rank, mpi_size, omp_thread, omp_num_threads);
}
// MPI结束
MPI_Finalize();
return 0;
}
# 编译 (需要支持OpenMP的MPI编译器)
mpicc -fopenmp hybrid_hello.c -o hybrid_hello
# 运行 (4个MPI进程,每个进程4个OpenMP线程)
export OMP_NUM_THREADS=4
mpirun -np 4 ./hybrid_hello
# 将MPI进程绑定到socket,OpenMP线程绑定到核心
mpirun -np 4 --bind-to socket ./hybrid_hello : \
-cpus-per-proc 4 --bind-to core
// MPI进程间数据划分
int global_size = 1000;
int local_size = global_size / mpi_size;
int start = mpi_rank * local_size;
// OpenMP线程间数据划分
#pragma omp parallel for
for (int i = start; i < start + local_size; i++) {
// 计算任务
}
if (mpi_rank == 0) {
// Master进程分配任务
#pragma omp parallel for
for (int i = 1; i < mpi_size; i++) {
MPI_Send(&task, ..., i, ..., MPI_COMM_WORLD);
}
} else {
// Worker进程处理任务
MPI_Recv(&task, ..., 0, ..., MPI_COMM_WORLD, ...);
#pragma omp parallel for
for (int i = 0; i < task_size; i++) {
// 并行处理接收到的任务
}
}
// MPI进程间域分解
MPI_Dims_create(mpi_size, 2, dims);
MPI_Cart_create(MPI_COMM_WORLD, 2, dims, periods, 0, &cart_comm);
// 每个MPI进程内部OpenMP并行计算
#pragma omp parallel for collapse(2)
for (int i = local_start_x; i < local_end_x; i++) {
for (int j = local_start_y; j < local_end_y; j++) {
// 计算网格点(i,j)
}
}
负载均衡:
MPI进程间负载均衡
OpenMP线程间负载均衡
通信优化:
// 使用MPI_THREAD_MULTIPLE提高线程安全
int provided;
MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &provided);
内存访问优化:
#pragma omp parallel for schedule(static) // 静态调度
for (...) { ... }
线程安全问题:
确保MPI调用在关键区域内
使用MPI_THREAD_MULTIPLE
级别
性能下降:
# 调整OpenMP线程数
export OMP_NUM_THREADS=$(($(nproc)/$MPI_SIZE))
死锁问题:
避免多线程同时调用阻塞式MPI通信
优先使用非阻塞通信MPI_Isend/MPI_Irecv
void matrix_multiply(double *A, double *B, double *C, int N) {
#pragma omp parallel for collapse(2)
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
double sum = 0.0;
for (int k = 0; k < N; k++) {
sum += A[i*N+k] * B[k*N+j];
}
C[i*N+j] = sum;
}
}
}
int main(int argc, char *argv[]) {
MPI_Init(&argc, &argv);
// 矩阵分块分发 (MPI)
// 每个进程计算自己的块 (OpenMP)
// 收集结果 (MPI)
MPI_Finalize();
return 0;
}
这种混合编程模型特别适合现代多核集群架构,能够充分利用节点内的多核资源和节点间的分布式内存资源。