MPI 叢集背景、Stencil 執行範例與 MPI Basics

重點總覽 (Overview)

項目 重點 對應 CUDA 概念
問題規模 單一 host+device 不夠,HPC 需要 cluster(多 node、每 node 多 host/device)的聚合運算力 從單卡擴展到多節點
記憶體模型 MPI 假設 distributed memory:process 間只能靠訊息傳遞 (message passing) 交換資料,不能共享變數 與 CUDA 的 global memory 不同(非共享)
程式模型 SPMD:所有 process 跑同一支程式,靠 rank 走不同分支 CUDA kernel 也是 SPMD(靠 threadIdx 分支)
行程識別 MPI rank / process id(0 ~ np-1,一維) blockIdx.x*blockDim.x + threadIdx.x
執行範例 3D 25-point stencil(Jacobi 熱傳導),沿 z 維度 切成 domain partitions 08-Stencil/01-Stencil-Background-and-Basic-Kernel
資料邊界 鄰近 partition 需互換 halo cells(每方向 4 片 slice);最外緣用 ghost cells(補 0) convolution / stencil 的 halo
角色分工 1 個 data server(最大 rank, np-1)負責 I/O;其餘 np-1 個 compute process 算 stencil 某些工作只由 thread 0 做
Important

本章把「單卡 CUDA kernel」放大成「多節點叢集」:核心三件事是 domain partitioning(切資料)、point-to-point / collective communication(換資料)。本篇只涵蓋 20.1–20.3 的背景、範例與啟動 API;通訊細節見 sibling notes。


為何叢集需要 MPI/CUDA (Background)

對照 Shared memory (單機/CUDA) Distributed memory (MPI)
資料交換 直接讀寫共同位址 送/收訊息 (send/recv)
程式員看到 同一位址空間 各 process 獨立位址空間
同步 __syncthreads() / barrier MPI_Barrier()(collective)
            ┌──────── Node 0 ────────┐   ┌──────── Node 1 ────────┐
            │  (proc)      (proc)     │   │  (proc)      (proc)     │
            │   CPU+GPU     CPU+GPU   │   │   CPU+GPU     CPU+GPU   │
            └───────┬────────┬───────┘   └───────┬────────┬───────┘
                    │        └──── messages ──────┘        │
                    └─────────── interconnect ─────────────┘
   每個 node 內含 1+ 個 process(雲朵);process 間靠「訊息」傳資料
Tip

程式員不需處理 interconnect 細節:呼叫通訊 API,MPI 實作負責路由。這讓同一份程式能在不同網路拓樸的叢集上跑。


Stencil 執行範例與 Domain Partitioning (A Running Example)

25-point stencil 是什麼

neighbors=4 (每方向)×6 (方向)=24,stencil=24+1 (自己)=25 points
            (i,j,k+4)              z 方向 ±1..±4
               ⋮                   y 方向 ±1..±4
  (i-4..i+4, j, k) ─●─ (i, j±1..±4, k)   x 方向 ±1..±4
               ⋮
            (i,j,k-4)        中心 ● 連同 24 鄰居 = 25 點

記憶體佈局 (row-major)

 範例 2(x)×2(y)×4(z):先放 z=0 整片,再 z=1,依此類推
 index: 0  1 | 2  3 || 4  5 | 6  7 || 8  9 |10 11 ||12 13|14 15
        └z=0 slice ┘   └z=1 slice ┘   └z=2 slice ┘  └z=3 slice┘
   (每片 = dimx*dimy = 4 個元素,沿 x 連續,y 次之)

沿 z 維度切 Domain Partitions

Halo cells 與 Ghost cells

            D(i-1)            D(i)  (一個 internal partition)         D(i+1)
        ┌───────────┐  ┌─────┬───────────────────┬─────┐  ┌───────────┐
        │  ... bdry │→ │halo │   internal points │halo │ ←│ bdry ...  │
        │  (4 片)   │  │ 4片 │  dimx*dimy*(P)     │ 4片 │  │  (4 片)   │
        └───────────┘  └─────┴───────────────────┴─────┘  └───────────┘
                          ↑左 halo 來自 D(i-1)    右 halo 來自 D(i+1)↑
   internal process 兩側都有鄰居 → +8 片;edge process 只一側 → +4 片
Warning

Edge vs Internal process 的 halo 數不同:最外緣(process 0 與 np-2)只有一側鄰居,另一側用 ghost cells(補 0,如 convolution),不需收資料。


MPI Basics 與 SPMD 設定 (MPI Basics)

五個建立/拆除通訊系統的核心函式

函式 作用 關鍵參數
MPI_Init(&argc,&argv) 初始化所有 process 的通訊系統 命令列引數
MPI_Comm_rank(comm,&pid) 回傳本 process 的唯一 rank communicator、輸出 pid
MPI_Comm_size(comm,&np) 回傳 communicator 內總 process 數 communicator、輸出 np
MPI_Abort(comm,code) 中止通訊並回傳錯誤碼(非 0 = 出錯) scope、error code
MPI_Finalize() 釋放所有 MPI 通訊資源
Important

Communicator (MPI_Comm) = 一群 process 的通訊範圍。預設 MPI_COMM_WORLD 代表「跑這支程式的所有 process」。MPI_Comm 等型別定義在 mpi.h(所有用 MPI 的 C 檔都要 #include)。

SPMD 主程式(Fig. 20.6)

#include "mpi.h"
int main(int argc, char *argv[]) {
    int pad = 0, dimx = 480+pad, dimy = 480, dimz = 400, nreps = 100;
    int pid = -1, np = -1;
    MPI_Init(&argc, &argv);
    MPI_Comm_rank(MPI_COMM_WORLD, &pid);   // 本 process 的 rank
    MPI_Comm_size(MPI_COMM_WORLD, &np);    // 總 process 數
    if (np < 3) {                          // 本例至少需 3 個 process
        if (0 == pid) printf("Needed 3 or more processes.\n"); // 只由 rank0 報錯
        MPI_Abort(MPI_COMM_WORLD, 1); return 1;
    }
    if (pid < np - 1)
        compute_process(dimx, dimy, dimz / (np - 1), nreps);   // 計算行程
    else
        data_server(dimx, dimy, dimz);                         // 資料伺服器 (rank np-1)
    MPI_Finalize();
    return 0;
}

Data server / Compute process 分工

   np 個 process (rank 0 .. np-1)
   ├─ rank 0 .. np-2  →  compute_process()   ← 算 stencil,共 np-1 個
   └─ rank np-1       →  data_server()        ← 做 I/O / 分發+收集資料
   注意:傳給 compute 的 dimz 是 dimz/(np-1)(每個只負責一片 partition)
Warning

不是所有 np 個 process 都在計算:最大 rank(np-1)被保留當 data server 做 I/O,真正算 stencil 的只有 np-1。這是設計決策,compute 端與 server 端的程式碼都依此一致。

Tip

同一份程式靠 rank 分支走不同 function call path(compute vs server)——這正是 SPMD:同碼、不同流程,與 CUDA kernel 內用 thread id 分支同理。


考試/面試重點 (Exam / Test Patterns)

情境 / 關鍵字 答案 / 技巧
「MPI 用哪種記憶體模型?」 Distributed memory + message passing;process 不能透過 shared memory 存同一變數
「MPI rank 對應 CUDA 的什麼?」 blockIdx.x*blockDim.x + threadIdx.x(但 rank 是一維
「25-point stencil 怎麼來的?」 6 方向 × 每方向 4 鄰居 = 24,加自己 = 25
「np 個 process 有幾個在算?」 np-1 個 compute;rank np-1 是 data server
「沿哪一維切?為何?」 沿 z;因 row-major 下一個 z slice 連續,切割/傳輸最自然
「internal vs edge 需多少 halo?」 internal +8 片(兩側各 4);edge +4 片(單側,另一側 ghost 補 0)
「為何要檢查 np?」 系統不保證開到使用者要求的 process 數,需 MPI_Comm_size 驗證
「五大設定 API?」 MPI_Init / MPI_Comm_rank / MPI_Comm_size / MPI_Abort / MPI_Finalize
「只報一次錯怎麼做?」 if(0==pid) printf(...)(只由 rank 0),等同 CUDA thread 0
「MPI_COMM_WORLD 是什麼?」 預設 communicator,涵蓋所有跑此程式的 process