Skip to content

MPI 编程实战:从 Hello 到并行归约 ​

摘要:上一篇只打印,这一篇真正并行算。以"全集群求和"为例子,从点对点通信(Send/Recv)演进到集合通信(MPI_Reduce),给出完整可编译代码、运行方式与结果验证。

1. 场景:把"求和"并行化 ​

需求:N 个进程各自持有数值 xrank=rank+1x_{\text{rank}} = \text{rank}+1,计算全集群总和。

  • 串行:一个人累加
  • 并行:各进程算自己的 → 汇总到 0 号进程

2. 版本一:点对点(MPI_Send / MPI_Recv) ​

c
#include <mpi.h>
#include <stdio.h>

int main(int argc, char* argv[])
{
    MPI_Init(NULL, NULL);

    int size, rank;
    MPI_Comm_size(MPI_COMM_WORLD, &size);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);

    int value = rank + 1;              // 每个进程自己的数
    int sum   = 0;

    if (rank == 0) {
        sum = value;                   // 0 号先收自己
        for (int i = 1; i < size; i++) {
            int recv;
            MPI_Recv(&recv, 1, MPI_INT, i, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
            sum += recv;
        }
        printf("Total sum = %d\n", sum);
    } else {
        MPI_Send(&value, 1, MPI_INT, 0, 0, MPI_COMM_WORLD);
    }

    MPI_Finalize();
    return 0;
}
bash
mpicc -o mpi_sum mpi_sum.c
mpirun -np 8 ./mpi_sum
# Total sum = 36        (1+2+...+8)

特点:逻辑直观,但通信是串行的(0 号进程逐个收)。进程一多,0 号会成为瓶颈。

3. 版本二:集合通信(MPI_Reduce) ​

c
#include <mpi.h>
#include <stdio.h>

int main(int argc, char* argv[])
{
    MPI_Init(NULL, NULL);

    int size, rank;
    MPI_Comm_size(MPI_COMM_WORLD, &size);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);

    int value = rank + 1;
    int sum   = 0;

    // 所有进程的值按 SUM 操作归约到 0 号进程
    MPI_Reduce(&value, &sum, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);

    if (rank == 0)
        printf("Total sum (Reduce) = %d\n", sum);

    MPI_Finalize();
    return 0;
}
bash
mpicc -o mpi_sum2 mpi_sum2.c
mpirun -np 8 ./mpi_sum2
# Total sum (Reduce) = 36

一行 MPI_Reduce 替代全部 Send/Recv,底层用树形聚合,扩展性远好于点对点版本。这就是为什么真实 HPC 应用几乎全用集合通信。

4. 集合通信速查 ​

函数作用常用场景
MPI_Bcast广播:根进程发给所有人分发参数/矩阵
MPI_Reduce归约:所有人 → 根进程聚合求和、求最大值、全局误差
MPI_Allreduce归约并广播给所有人每个进程都要最终结果
MPI_Scatter分发给各进程一部分数据分块
MPI_Gather各进程数据收到根进程收集结果
MPI_Barrier同步屏障阶段对齐

5. 运行注意 ​

5.1 进程数与节点 ​

bash
# 8 个进程:4 节点 × 每节点 2 进程
mpirun -perhost 2 -machinefile hostfile -np 8 ./mpi_sum2

5.2 多节点 fabric(与 hello world 一致) ​

bash
# 以太网
mpirun -perhost 2 -np 8 -genv I_MPI_FABRICS shm:ofi -genv FI_PROVIDER tcp -machinefile hostfile ./mpi_sum2
# InfiniBand
mpirun -perhost 2 -np 8 -genv I_MPI_FABRICS shm:ofi -genv FI_PROVIDER mlx  -machinefile hostfile ./mpi_sum2

5.3 结果验证 ​

  • 串行校验:1+2+⋯+N=N(N+1)/21+2+\cdots+N = N(N+1)/2,与程序输出对照。
  • 换 -np 跑多次,确认结果一致——这是 MPI 正确性的第一关。

6. 从求和到真实负载 ​

这套模式稍加扩展就是真实应用:

  • 求全局误差/范数:MPI_Allreduce(MPI_SUM) 收集各进程局部误差
  • 并行矩阵运算:MPI_Scatter 分块 + 局部计算 + MPI_Gather 收结果(HPL 的雏形)
  • 数值模拟:每进程算自己的网格,MPI_Sendrecv 交换边界

7. 小结 ​

  • 点对点(Send/Recv)适合少量、定向通信;集合通信(Reduce/Bcast 等)适合大规模。
  • 并行程序验证三步走:小规模跑通 → 换进程数结果一致 → 与串行结果对照。

至此,"CPU 理论计算 → 性能预估 → HPL 实测 → MPI 环境 → Hello → 编程"已构成完整的 CPU 实战链路。相关指南:CPU HPL 测试指南 (Intel)。

AI-HPC 智算超算专家 · Contact: openaihpc@gmail.com