一、MPI 是什麼?

1.  並行計算的三種模式

模式

特點

典型工具

共享內存(Shared Memory)

多線程訪問同一內存空間

OpenMP, Pthreads

分佈式內存(Distributed Memory)

每個節點有獨立內存,通過網絡通信

MPI

混合模型(Hybrid)

結合兩者優勢,節點內用 OpenMP,節點間用 MPI

MPI + OpenMP/CUDA

MPI:主要用於分佈式內存系統,適合跨多個服務器節點的大規模並行任務。

2.  MPI 的設計理念

MPI 是一種標準化的消息傳遞庫接口,定義了進程之間如何發送和接收數據。其核心思想是:

  • 每個計算單元是一個獨立的進程(process)
  • 進程之間不共享內存,必須通過顯式調用 MPI_Send / MPI_Recv 來交換信息
  • 所有通信操作都基於“通信子”(communicator),最常用的是 MPI_COMM_WORLD

關鍵優勢:

  • 可擴展性強:可運行於雙核筆記本到百萬核超算
  • 跨平台兼容:支持Linux、Windows、macOS和各種架構(x86、ARM、GPU)
  • 生態成熟:幾乎所有科學計算軟件底層都依賴 MPI

3.  MPI 的應用地位

應用領域

使用場景

是否依賴 MPI

氣象預報(WRF)

大氣網格劃分與同步更新

流體力學(OpenFOAM)

分佈式求解 Navier-Stokes 方程

分子動力學(LAMMPS)

粒子間力的並行計算

地震波模擬(SPECFEM3D)

波場傳播的域分解

AI 數據預處理

分佈式讀取 TFRecord/HDF5 文件

可選但高效

統計數據:據 Open MPI 官方報告,超過 90% 的 Top500 超算系統默認安裝 MPI 實現。

(1) HPC 集羣典型架構圖

毅碩HPC | 一文詳解HPC環境中的MPI並行計算_高性能計算

  • 橙色線:通常代表管理和控制流程。它連接了管理節點(包含Slurm控制器和LDAP認證)到計算節點和存儲系統。這些連線用於傳輸作業調度指令、用户認證信息、監控數據以及配置管理等控制信號。
  • 藍色線:通常代表數據和用户交互流程。它連接了用户端到登錄節點,登錄節點到管理節點,以及計算節點到存儲系統和高速互聯網絡。這些連線用於傳輸用户上傳/下載的文件、計算節點讀取/寫入的數據、以及計算節點之間的高速通信數據(如MPI消息)。

(2) MPI 點對點通信流程圖

毅碩HPC | 一文詳解HPC環境中的MPI並行計算_高性能計算_02

  • 方向:單向傳輸(阻塞模式)
  • 用途:主從結構中的參數下發、結果回收
  • 阻塞表現:
  • 在 Rank 1,進入 MPI_Recv 到 數據到達 之間的時間段,進程處於等待狀態,不能做其他事情,這就是阻塞接收。
  • 在 Rank 0,進入 MPI_Send 到 發送完成 之間,進程必須確保數據安全發出(通常意味着發送緩衝區可以安全修改了)才能繼續,這也是阻塞發送。

發送先於接收:雖然兩個進程可能在不同時間點調用函數,但數據傳輸動作本身(斜線箭頭)必須始於發送方,終於接收方。Rank 1 即使很早就調用了 Recv,也必須等到 T4 時刻數據真正到達才算完成。

(3) MPI 集合通信 — Allreduce 示例

毅碩HPC | 一文詳解HPC環境中的MPI並行計算_安裝教程_03

所有進程最終獲得相同的結果(如梯度平均),常用於 AI 分佈式訓練。


二、搭建你的第一個 MPI 開發環境

1.  安裝 MPI 實現庫(推薦 OpenMPI 或 MPICH)

  • Ubuntu/Debian
sudo apt update
sudo apt install openmpi-bin libopenmpi-dev
  • CentOS/RHEL/Rocky Linux
sudo dnf install openmpi openmpi-devel

CentOS 7推薦安裝 openmpi3 ,openmpi 的版本過低:

sudo yum install openmpi3 openmpi3-devel

推薦選擇 OpenMPI:社區活躍、文檔豐富、支持 GPU 直接通信(CUDA-aware)

2.  編譯與運行環境配置

  • 加載openmpi模塊:
module load mpi/openmpi3-x86_64

如果出現 -bash: module: command not found,使用source /etc/profile.d/modules.sh再加載

  • 確保已安裝:
  • GCC 編譯器(gccg++ )
  • mpicc(MPI C 編譯器封裝腳本,安裝openmpi3-devel即默認安裝)
  • 驗證安裝:
mpirun --version
mpicc --showme

輸出應類似: 

mpirun (Open MPI) 3.1.3

 ...

3.  在本地多核機器上測試 MPI 程序

創建測試目錄:

mkdir ~/mpi-demo && cd ~/mpi-demo

編寫一個簡單的 hello.c

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

int main(int argc, char** argv) {
    MPI_Init(&argc, &argv);

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

    printf("Hello from process %d of %d\n", rank, size);

    MPI_Finalize();
    return 0;
}

編譯並運行:

mpicc -o hello hello.c
mpiexec -n 8 ./hello

輸出:

毅碩HPC | 一文詳解HPC環境中的MPI並行計算_HPC_04

如果出現:

[1764820148.412669] [compute01:10228:0]            sys.c:618  UCX  ERROR shmget(size=2097152 flags=0xfb0) for mm_recv_desc failed: Operation not permitted, please check shared memory limits by 'ipcs -l'
[1764820148.414829] [compute01:10229:0]            sys.c:618  UCX  ERROR shmget(size=2097152 flags=0xfb0) for mm_recv_desc failed: Operation not permitted, please check shared memory limits by 'ipcs -l'
[1764820148.412682] [compute01:10230:0]            sys.c:618  UCX  ERROR shmget(size=2097152 flags=0xfb0) for mm_recv_desc failed: Operation not permitted, please check shared memory limits by 'ipcs -l'
[1764820148.414706] [compute01:10233:0]            sys.c:618  UCX  ERROR shmget(size=2097152 flags=0xfb0) for mm_recv_desc failed: Operation not permitted, please check shared memory limits by 'ipcs -l'
[1764820148.416116] [compute01:10235:0]            sys.c:618  UCX  ERROR shmget(size=2097152 flags=0xfb0) for mm_recv_desc failed: Operation not permitted, please check shared memory limits by 'ipcs -l'
[1764820148.421206] [compute01:10236:0]            sys.c:618  UCX  ERROR shmget(size=2097152 flags=0xfb0) for mm_recv_desc failed: Operation not permitted, please check shared memory limits by 'ipcs -l'
[1764820148.421431] [compute01:10237:0]            sys.c:618  UCX  ERROR shmget(size=2097152 flags=0xfb0) for mm_recv_desc failed: Operation not permitted, please check shared memory limits by 'ipcs -l'
[1764820148.421575] [compute01:10239:0]            sys.c:618  UCX  ERROR shmget(size=2097152 flags=0xfb0) for mm_recv_desc failed: Operation not permitted, please check shared memory limits by 'ipcs -l'
Hello from process 0 of 8
Hello from process 1 of 8
Hello from process 2 of 8
Hello from process 3 of 8
Hello from process 4 of 8
Hello from process 5 of 8
Hello from process 6 of 8
Hello from process 7 of 8

這是OpenMPI + UCX(Unified Communication X)在 CentOS 7 上因共享內存限制導致的警告:

  • 查看當前共享內存限制

ipcs -l

------ Messages Limits --------
max queues system wide = 32000
max size of message (bytes) = 8192
default max size of queue (bytes) = 16384

------ Shared Memory Limits --------
max number of segments = 4096
max seg size (kbytes) = 18014398509465599
max total shared memory (kbytes) = 18014398442373116
min seg size (bytes) = 1

------ Semaphore Limits --------
max number of arrays = 128
max semaphores per array = 250
max semaphores system wide = 32000
max ops per semop call = 32
semaphore max value = 32767

可以看到:max seg size (kbytes) = 18014398509465599,限制已經非常大(接近無限),不用修改

  • 檢查 /etc/security/limits.conf
nano /etc/security/limits.conf
# 添加如下內容:

# Increase SHM limits for MPI
*          soft    memlock         unlimited
*          hard    memlock         unlimited
*          soft    nofile          65536
*          hard    nofile          65536
  • 步驟 3:啓用 PAM limits
# 確保 SSH 登錄時加載 limits。編輯:
sudo nano /etc/ssh/sshd_config
# 確認包含:
UsePAM yes
# 若沒有包含,則添加後重啓 sshd:
sudo systemctl reload sshd

三、MPI 基礎編程入門(C語言為例)

1.  初始化與終止

MPI_Init(&argc, &argv);        // 必須第一個調用
// ... 中間寫並行邏輯 ...
MPI_Finalize();                // 必須最後一個調用

2.  獲取進程身份

int rank, size;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);   // 當前進程編號(從0開始)
MPI_Comm_size(MPI_COMM_WORLD, &size);   // 總共多少個進程

3.  點對點通信:發送與接收

if (rank == 0) {
    int data = 100;
    MPI_Send(&data, 1, MPI_INT, 1, 0, MPI_COMM_WORLD);
} else if (rank == 1) {
    int buf;
    MPI_Recv(&buf, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
    printf("Received: %d\n", buf);
}

注意MPI_Recv 必須等待對應 Send 到達才會返回(阻塞式)

4.  集合通信初探

(1) 廣播(Broadcast)

int value;
if (rank == 0) value = 42;
MPI_Bcast(&value, 1, MPI_INT, 0, MPI_COMM_WORLD);  // 所有進程都得到 value=42

(2) 歸約(Reduce)—— 主從結構彙總

int local_sum = rank * 10;
int global_sum;
MPI_Reduce(&local_sum, &global_sum, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);

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

(3) 數據分發與收集

int send_data[4] = {10, 20, 30, 40};
int recv_item;

MPI_Scatter(send_data, 1, MPI_INT, &recv_item, 1, MPI_INT, 0, MPI_COMM_WORLD);
printf("Rank %d received %d\n", rank, recv_item);

輸出(假設 n=4):
Rank 0 received 10
Rank 1 received 20
Rank 2 received 30
Rank 3 received 40


四、編譯與運行 MPI 程序

1.  編譯命令

mpicc -o myprogram myprogram.c        # 編譯
mpiexec -n 8 ./myprogram              # 運行8個進程

2.  本地跨核運行

強制綁定到特定CPU核心(提升緩存效率)

mpiexec --bind-to core -n 4 ./hello

3.  跨節點運行前提

  • 所有節點安裝相同版本的 MPI
  • 配置無密碼 SSH 通信
  • 使用共享文件系統(NFS/Lustre),保證每個節點都能訪問可執行文件

五、在真實 HPC 集羣中運行 MPI 作業( Slurm 為例 )

1.  HPC 集羣典型工作流

[用户] → 編輯代碼 → 提交 .job 腳本 → [Slurm 調度器] → 分配資源 → srun 啓動 mpiexec → 計算節點運行 → 輸出日誌

2.  編寫 Slurm 批處理腳本

保存為 run_mpi.job

#!/bin/bash
#SBATCH --job-name=mpi_hello
#SBATCH --nodes=2
#SBATCH --ntasks-per-node=8
#SBATCH --time=00:10:00
#SBATCH --output=hello_%j.out
#SBATCH --error=hello_%j.err

# 加載模塊(根據系統調整)
module load openmpi/openmpi3-x86_64

# 編譯(可選:也可提前編譯好)
mpicc -o hello hello.c

# 啓動作業
srun mpiexec -n 16 ./hello

參數説明:

  • --nodes=2:使用2個計算節點
  • --ntasks-per-node=8:每節點啓動8個 MPI 進程
  • 總共 2×8=16 個進程

3.  提交與監控作業

  • 提交
sbatch run_mpi.job
  • 查看隊列
squeue -u $USER
  • 查看已完成作業統計
sacct -j <jobid>
  • 查看輸出
cat hello_*.out

4.  常見問題與解決方案

問題

原因

建議

Command 'mpicc' not found

模塊未加載

添加 module load openmpi

作業長時間 pending

隊列擁塞

使用 sinfo 查看可用資源

運行時報錯 “connection closed”

SSH 或 OFED 驅動異常

聯繫管理員檢查 InfiniBand 狀態


六、進階主題與最佳實踐

1.  非阻塞通信:提升並行效率

MPI_Request req;
MPI_Isend(buffer, count, MPI_DOUBLE, dest, tag, MPI_COMM_WORLD, &req);

// 做其他計算...
do_local_work();

// 等待發送完成
MPI_Wait(&req, MPI_STATUS_IGNORE);

優點:通信與計算重疊,提高資源利用率

2.  性能調優建議

技巧

説明

合併小消息

減少通信次數,提升帶寬利用率

使用拓撲通信

如 Cartesian topology 優化鄰域通信

避免熱點進程

均衡負載,防止主節點成為瓶頸

啓用 CUDA-aware MPI

GPU 顯存直傳,避免主機中轉

3.  容器化支持:Apptainer/Singularity 中運行 MPI

  • 構建包含 MPI 的容器鏡像(Singularity definition file)
Bootstrap: docker
From: ubuntu:20.04

%post
    apt update
    apt install -y openmpi-bin libopenmpi-dev gcc

%runscript
    exec mpiexec "$@"
  • 構建並運行
singularity build mpi_container.sif Singularity.def
srun singularity run mpi_container.sif -n 16 ./my_mpi_app

優勢:環境隔離、可復現、便於部署複雜依賴


七、真實行業案例解析

案例一:OpenFOAM 流體仿真中的 MPI 應用

  • 背景:模擬汽車風阻係數
  • 方法
  • 使用 decomposePar 將網格劃分為多個子域
  • 每個子域由一個 MPI 進程負責計算
  • 邊界數據通過 MPI 實時交換
  • 效果:原本需 72 小時的仿真縮短至 6 小時(使用 128 核)

案例二:天文 N 體模擬(Gadget-2)

  • 挑戰:百億粒子間的引力計算
  • MPI 角色
  • Domain Decomposition 劃分空間區域
  • All-to-All 通信交換遠程粒子信息
  • Tree Algorithm 與 MPI 結合實現長程力計算
  • 成果:成功模擬宇宙大尺度結構形成

案例三:金融蒙特卡洛期權定價

Python 偽代碼(通過 mpi4py)

from mpi4py import MPI

import numpy as np

comm = MPI.COMM_WORLD

rank = comm.Get_rank()

size = comm.Get_size()

# 每個進程生成 10000 條路徑

local_paths = generate_paths(n=10000)

local_price = np.mean(local_paths)

# 全局平均

global_price = comm.reduce(local_price, op=MPI.SUM, root=0)

if rank == 0:

    final_price = global_price / size

    print(f"期權價格估計: {final_price:.4f}")

本地機器8個進程測試:

mpiexec -n 8 python option_pricing.py

毅碩HPC | 一文詳解HPC環境中的MPI並行計算_高性能計算_05

效果:100 萬次模擬僅運行3.09秒(使用 8 進程)


八、總結

MPI 不僅僅是一種編程接口,它是連接算法與硬件之間的橋樑,是實現“算得更快、看得更遠”的關鍵技術支撐。 通過本教程的學習,你應該已經能夠:

  • 理解 MPI 在 HPC 生態系統中的核心地位
  • 編寫基礎的 MPI 程序並進行點對點與集合通信
  • 在本地和 HPC 集羣上成功編譯、運行和調試 MPI 作業
  • 理解其在科學計算與工程仿真中的典型應用場景

但這只是起點。隨着異構計算(GPU+CPU)、混合編程模型(MPI + OpenMP/CUDA)的發展,MPI 正在與其他並行範式深度融合。