Skip to content
Gains Summary
Main Navigation 首页 / Home
C++ 编程 / C++ Programming
系统与高性能 / Systems & Performance
Web 开发 / Web Development
人工智能 / Artificial Intelligence
工业软件 / Industrial Software
其他内容 / Other Topics
C++ 编程 / C++系统与性能 / SystemsWeb 开发 / Web人工智能 / AI工业软件 / Industrial

外观

Sidebar Navigation

← 系统与高性能 / Systems & Performance

计算系统 / Computing Systems

1. 计算系统:计算机如何执行与加速程序

2. 从 C++ 源码到 CPU 执行

3. CPU 流水线、乱序执行与分支预测

4. Cache、一致性、伪共享与 NUMA

5. GPU、SM、Warp 与显存

6. 计算执行模型:程序怎样映射到机器

7. SIMD 与编译器向量化

8. C++ 多线程与 OpenMP

9. CUDA 平台与编程模型

10. CUDA Kernel、内存与性能

11. CPU-GPU 异构流水线

12. MPI 与分布式并行

13. 并行算法模式

14. 性能模型与工具

15. 递进学习项目:从单线程到集群

历史完整正文 / Original Deep Dives

1. 历史完整正文:统一前文章逐篇保留

原体系结构与硬件 / Original Architecture

1. 硬件编程与高性能计算:一张可走通的学习地图 / A Practical Learning Map for Hardware Programming and HPC

2. 计算机体系结构:CPU、内存与 GPU / Computer Architecture: CPUs, Memory, and GPUs

3. 计算机架构基础——为什么 GPU 比 CPU 更快 / Computer Architecture Fundamentals: Why GPUs Outperform CPUs

4. 并行计算理论——30 天训练能优化到多快? / Parallel Computing Theory and the Limits of Training Acceleration

5. GPU 架构深入——上万个核心如何分工协作 / GPU Architecture and Massive Parallel Execution

6. CUDA 编程模型——把矩阵乘法映射到 GPU / The CUDA Programming Model for Mapping Matrix Multiplication to GPUs

7. CUDA 内存管理——百亿参数如何装进显存 / CUDA Memory Management for Large Models

8. CUDA 性能优化——从 30 天缩短到 10 天 / CUDA Performance Optimization

9. CPU 并行编程——OpenMP 与 SIMD 向量化 / CPU Parallel Programming with OpenMP and SIMD

10. HPC 集群与 MPI——多节点分布式训练 / HPC Clusters and MPI for Distributed Training

11. 异构计算——CPU 与 GPU 如何协同工作 / Heterogeneous Computing with CPUs and GPUs

12. 深度学习训练优化实战——从 30 天到 3 天 / Deep Learning Training Optimization from Thirty Days to Three

13. 性能分析工具——找到真正的瓶颈 / Performance Analysis Tools for Finding Real Bottlenecks

14. NPU 全景——昇腾/寒武纪/TPU/苹果生态 / The NPU Landscape: Ascend, Cambricon, TPU, and Apple

15. 未来趋势——2030 年的计算机会是什么形态 / Future Computing Trends Toward 2030

16. 硬件与高性能计算:从“程序为什么慢”开始 / Hardware and HPC Starting from Why Programs Are Slow

原并行计算 / Original Parallel Computing

1. 并行计算:从 SIMD 到 MPI / Parallel Computing from SIMD to MPI

2. 并行计算全景:从晶体管、CPU、GPU 到计算集群 / Parallel Computing from Transistors, CPUs, and GPUs to Clusters

3. 并行计算基础:任务分解、加速比与可扩展性 / Parallel Computing Fundamentals: Decomposition, Speedup, and Scalability

4. 处理器体系结构:从指令流水线到多核芯片 / Processor Architecture from Instruction Pipelines to Multicore Chips

5. CPU 并行:多线程、SIMD、Cache 一致性与 NUMA / CPU Parallelism with Threads, SIMD, Cache Coherence, and NUMA

6. 内存层次:Cache、带宽、局部性与一致性 / Memory Hierarchies, Bandwidth, Locality, and Coherence

7. GPU 体系结构:SIMT、Warp、SM 与吞吐优先设计 / GPU Architecture with SIMT, Warps, and Streaming Multiprocessors

8. CUDA 编程模型:Thread、Block、Grid 与内存协作 / CUDA Threads, Blocks, Grids, and Cooperative Memory Access

9. 并行算法模式:Map、Reduce、Scan、Stencil 与任务图 / Parallel Patterns: Map, Reduce, Scan, Stencil, and Task Graphs

10. 异构计算:CPU、GPU、NPU 如何协同工作 / Heterogeneous Computing with CPUs, GPUs, and NPUs

11. 分布式并行:MPI、集合通信、RDMA 与多机多卡 / Distributed Parallelism with MPI, Collective Communication, and RDMA

12. 性能工程:测量、Roofline、瓶颈定位与优化闭环 / Performance Engineering with Measurement, Roofline, and Bottleneck Analysis

13. 并行计算实战:AI、CAE、图像与科学计算 / Parallel Computing for AI, CAE, Imaging, and Scientific Computing

14. 并行计算实践路线:从单核优化到多机多卡 / A Parallel Computing Project Path from Single-Core to Multi-Node GPUs

本页目录

MPI 与分布式并行 ​

MPI(Message Passing Interface)是一套消息传递标准,不是某一款编译器或网络协议。常见实现包括 Open MPI、MPICH 和厂商实现。

1. 为什么线程不够 ​

单机线程可以共享内存;跨机器没有一根普通 C++ 指针能直接访问另一节点的 RAM。MPI 把数据交换写成显式通信。

text
node A                         node B
rank 0: private address space rank 1: private address space
buffer A -- send/network -->  buffer B
1
2
3

Rank 是 communicator 内进程的编号;Communicator 定义一组可互相通信的进程及通信上下文。Rank 不是 CPU 核心,也不必等于物理机器编号。

2. 最小模型 ​

cpp
MPI_Init(&argc, &argv);
int rank, size;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
// local computation and communication
MPI_Finalize();
1
2
3
4
5
6

通常用 MPI 编译器包装器构建,由启动器创建多个进程。包装器负责传入实现所需头文件和库;它不是一种新的 C++ 语言。

3. 通信类型 ​

  • 点对点:Send/Recv,明确源、目标、tag 和 communicator;
  • 广播:一个 Rank 向组内发送;
  • Scatter/Gather:分发/收集不同片段;
  • Reduce/Allreduce:按运算合并局部值;
  • Barrier:所有进程到达后继续,不能用来替代真正的数据依赖。

Collective 必须由 communicator 中相关进程以兼容次序参与,否则可能死锁或产生错误。

4. 域分解与 Halo ​

Stencil 类问题常把网格分给多个 Rank:

text
rank 0 domain        rank 1 domain
[interior | halo] <-> [halo | interior]
1
2

每轮先交换边界,再计算。可以先计算不依赖新 Halo 的内部区域,同时进行非阻塞通信,最后等待边界到达并计算边缘。

5. 延迟、带宽与粒度 ​

常用通信模型:

text
T_message ~= latency + bytes / bandwidth
1

许多小消息反复支付 latency;过大的聚合又可能拖延流水。数据分区应同时减少总字节数、消息次数和负载不均。

6. MPI + OpenMP + CUDA ​

text
cluster
└── node
    ├── MPI ranks: process / address-space parallelism
    ├── OpenMP threads: shared-memory CPU parallelism
    └── CUDA: one or more GPUs
1
2
3
4
5

混合模型能贴合硬件层次,但会引入线程级别支持、GPU 绑定、NUMA 亲和性和通信重叠等复杂性。先建立单层正确基线,再逐层组合。

深入原理与工程实践 ​

前面的内容负责建立统一心智模型;下面把同一主题继续拆到执行过程、代码、性能代价与工程判断。

<!-- migrated-deep-dive:start -->

完整迁入:原 HPC 集群与 MPI 全文 ​

HPC 集群与 MPI——多节点分布式训练 / HPC Clusters and MPI for Distributed Training ​

📅 创建时间:2026-06-02 🏷️ 标签:#HPC #MPI #NCCL #NVLink #InfiniBand #分布式训练 📚 前置知识:[[02-parallel-computing-theory]](并行理论) [[07-openmp-simd]](单节点并行) 📚 相关知识:[[09-heterogeneous-computing]](异构计算) [[10-dl-training-optimization]](训练优化)


先抓住直觉 ​

多卡计算不只是“把任务分出去”,还要把中间结果交换回来。卡内、同机跨卡、跨机器三种距离的通信成本不同;规模越大,网络越可能取代计算成为瓶颈。

  • 必须理解:进程、节点和 GPU 的关系;点对点与集合通信;AllReduce 为什么昂贵。
  • 用到再查:MPI 函数签名、Ring 的每一步和 NCCL 环境变量。
  • 始终追问:每轮要传多少字节,走哪条链路,能否与计算重叠?

场景:单卡跑 30 天,8 卡能跑多久? ​

┌─────────────────────────────────────────────────────────────┐
│                                                             │
│  单卡 A100 训练 175B 模型:30 天                        │
│                                                             │
│  你的集群:                                                │
│  - 8 张 A100(每卡 80GB)                                │
│  - NVLink 连接(同节点 4 卡)                             │
│  - InfiniBand HDR 连接(跨节点)                         │
│                                                             │
│  问题:                                                    │
│  8 卡真的能快 8 倍吗?                                    │
│  如果不能,问题出在哪里?                                   │
│  如何充分利用集群的通信带宽?                               │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

第1节:HPC 集群硬件拓扑 ​

多节点集群架构 ​
┌─────────────────────────────────────────────────────────────┐
│                    HPC 集群硬件拓扑                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌──────────────────────────────────────────────────────┐  │
│  │                 Node 0 (A100 × 8)                    │  │
│  │                                                      │  │
│  │   GPU0───GPU1───GPU2───GPU3───GPU4───GPU5───GPU6──GPU7 │
│  │    │     │     │     │     │     │     │     │      │
│  │    └─────┴─────┴─────┴─────┴─────┴─────┴─────┘      │
│  │                   NVLink / NVSwitch                     │
│  │                        ↑                               │
│  │                  CPU Socket 0                          │
│  └────────────────────────┬───────────────────────────────┘  │
│                           │ PCIe                           │
│  ┌───────────────────────┴───────────────────────────────┐  │
│  │              InfiniBand HDR (100-200 GB/s)             │  │
│  └───────────────────────┬───────────────────────────────┘  │
│                          │                                 │
│  ┌───────────────────────┴───────────────────────────────┐  │
│  │                 Node 1 (A100 × 8)                    │  │
│  │   ...同 Node 0...                                    │  │
│  └──────────────────────────────────────────────────────┘  │
│                                                             │
│  带宽对比:                                                 │
│  NVLink(同节点): 600 GB/s (A100)                       │
│  PCIe: 32 GB/s                                            │
│  InfiniBand HDR: 100-200 GB/s                            │
│  千兆以太网: 0.125 GB/s                                   │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
通信模式对性能的影响 ​
┌─────────────────────────────────────────────────────────────┐
│                    同节点 vs 跨节点通信                       │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  同节点(NVLink):                                        │
│  GPU 0 → GPU 1: 600 GB/s                                │
│  → 几乎免费!                                             │
│                                                             │
│  跨节点(InfiniBand):                                   │
│  Node 0 → Node 1: 100-200 GB/s                         │
│  → 延迟高,带宽低                                         │
│                                                             │
│  实际训练影响:                                           │
│  - 梯度同步必须跨节点通信(AllReduce)                     │
│  - 跨节点通信成为主要瓶颈                                  │
│  - 需要仔细设计通信策略                                    │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

第2节:MPI——多进程通信标准 ​

MPI 基本概念 ​
┌─────────────────────────────────────────────────────────────┐
│                    MPI 是什么?                             │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  MPI = Message Passing Interface                           │
│  多进程并行编程标准                                         │
│                                                             │
│  每个进程有独立的内存空间(不像 OpenMP 共享内存)          │
│  进程间通信通过发送/接收消息                               │
│                                                             │
│  典型应用:                                               │
│  - HPC 集群(科学计算)                                   │
│  - 分布式深度学习训练                                     │
│                                                             │
│  与 OpenMP 的区别:                                       │
│  OpenMP: 共享内存,同一进程内多线程                       │
│  MPI: 分布式内存,多进程,可能在不同机器上               │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
点对点通信 ​
cpp
// MPI 点对点通信示例
#include <mpi.h>

int main(int argc, char** argv) {
    MPI_Init(&argc, &argv);  // 必须第一个调用

    int rank, size;
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);  // 当前进程 rank
    MPI_Comm_size(MPI_COMM_WORLD, &size);  // 总进程数

    if (rank == 0) {
        // 进程 0:发送数据
        int send_data = 42;
        MPI_Send(&send_data, 1, MPI_INT,  // 数据、个数、类型
                 1,  // 目标进程 rank
                 0,  // 标签(区分不同消息)
                 MPI_COMM_WORLD);
        printf("Process 0 sent: %d\n", send_data);
    } else if (rank == 1) {
        // 进程 1:接收数据
        int recv_data;
        MPI_Recv(&recv_data, 1, MPI_INT,  // 数据、个数、类型
                  0,  // 源进程 rank
                  0,  // 标签(必须匹配)
                  MPI_COMM_WORLD,
                  MPI_STATUS_IGNORE);  // 状态信息
        printf("Process 1 received: %d\n", recv_data);
    }

    MPI_Finalize();  // 必须最后一个调用
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
集合通信(Collective Communication) ​
cpp
// AllReduce:所有进程数据汇总后,结果同步到所有进程
// 深度学习训练中的梯度同步用这个!

float local_sum = compute_gradient();  // 每个进程计算本地梯度

float global_sum;
MPI_Allreduce(&local_sum,    // 输入(本地数据)
              &global_sum,   // 输出(汇总结果)
              1,             // 数据个数
              MPI_FLOAT,     // 数据类型
              MPI_SUM,       // 操作类型
              MPI_COMM_WORLD);

// 效果:
// Process 0: 100 ──┐
// Process 1: 200 ──┼── AllReduce(MPI_SUM) ──→ 所有人得到 900
// Process 2: 300 ──┤
// Process 3: 300 ──┘

// 用于梯度同步:
// 每个 GPU 计算本地梯度 → AllReduce 求和 → 每个 GPU 得到平均梯度
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
cpp
// Broadcast:根进程数据广播到所有进程
float model_param;
if (rank == 0) {
    model_param = load_from_disk();  // 只有 rank 0 加载
}
MPI_Bcast(&model_param, 1, MPI_FLOAT, 0, MPI_COMM_WORLD);
// → 所有进程都得到 model_param

// AllGather:收集所有进程的数据
float local_loss = compute_loss();
float all_losses[4];
MPI_Allgather(&local_loss, 1, MPI_FLOAT,
              all_losses, 1, MPI_FLOAT,
              MPI_COMM_WORLD);
// → all_losses = [loss0, loss1, loss2, loss3]

// Scatter:分发数据到不同进程
// Gather:从不同进程收集数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

第3节:NCCL——GPU 通信库 ​

NCCL 是什么? ​
┌─────────────────────────────────────────────────────────────┐
│                    NCCL 简介                                │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  NCCL = NVIDIA Collective Communications Library           │
│  NVIDIA 开发的 GPU 集合通信库                               │
│                                                             │
│  vs MPI:                                                  │
│  MPI:通用标准,CPU 内存通信                               │
│  NCCL:NVIDIA 专用,GPU 显存直接通信!                    │
│                                                             │
│  NCCL 通信不走 CPU,直接 GPU→GPU                           │
│  → 延迟更低,带宽更高                                       │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
NCCL AllReduce ​
cpp
// 使用 PyTorch 分布式(底层用 NCCL)
// torchrun --nproc_per_node=8 train.py

// Python 示例
import torch
import torch.distributed as dist

def main():
    dist.init_process_group(backend="nccl")  # 使用 NCCL

    rank = dist.get_rank()
    world_size = dist.get_world_size()

    # 每个 GPU 计算本地梯度
    local_grad = torch.randn(1000).cuda()

    # NCCL AllReduce(梯度同步)
    # 所有 GPU 的梯度相加,结果同步到所有 GPU
    dist.all_reduce(local_grad, op=dist.ReduceOp.SUM)
    local_grad /= world_size

    print(f"Rank {rank}: gradient synced, sum = {local_grad.sum()}")
    # 所有 rank 的 local_grad 现在都相同

    dist.destroy_process_group()

if __name__ == "__main__":
    main()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

第4节:Ring AllReduce——通信算法 ​

为什么 AllReduce 是瓶颈? ​
175B 参数模型,FP16:
- 每个 GPU 需要同步 350 GB 梯度
- 8 卡,每卡 AllReduce 传输量 = 350GB / 8 × 2 = 87.5 GB
- InfiniBand 带宽 200 GB/s → 需要 ~0.5 秒
- 如果每个迭代 1 秒,通信占比 = 50%!

必须优化 AllReduce 算法!
1
2
3
4
5
6
7
Ring AllReduce 算法 ​
┌─────────────────────────────────────────────────────────────┐
│                    Ring AllReduce(8 卡示例)                 │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Step 1: Scatter-Reduce(8 步)                          │
│  数据分成 8 份,P0持有 [0], P1持有[1], ..., P7持有[7]    │
│                                                             │
│  环形通信:每个 GPU 把自己的块发给下一个,同时累加收到的块  │
│                                                             │
│  Step 1: P0→P1: 累加 [0]    P1→P2: 累加 [1]  ...   │
│  Step 2: P1→P2: 累加 [0]    P2→P3: 累加 [1]  ...   │
│  ...                                                        │
│  Step 7: P6→P7: 累加 [0]    P7→P0: 累加 [7]  ...   │
│  → 每块被所有 GPU 累加一次                                  │
│                                                             │
│  Step 2: AllGather(8 步)                               │
│  把最终结果广播回所有 GPU                                  │
│  P0→P1: [0_final]  P1→P2: [1_final] ...            │
│  ...                                                        │
│  → 所有 GPU 最终持有完整的平均值                            │
│                                                             │
│  总通信量:2 × (N-1) × 数据量/N                          │
│  比 Tree AllReduce 少,适用于高带宽互联                   │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
NCCL 的自动优化 ​
cpp
// NCCL 会自动选择最优算法:
// - Ring AllReduce(高带宽互联,如同节点 NVLink)
// - Tree AllReduce(低带宽互联,如跨节点 InfiniBand)
// - CollNet(分层通信,同节点 Ring,跨节点 Tree)

// 查看 NCCL 使用的算法
// 设置 NCCL_DEBUG=INFO 环境变量
// export NCCL_DEBUG=INFO
// ./train

// 输出示例:
// NCCL INFO all_reduce : comm:0:0, op:SUM, nranks:8
// NCCL INFO Ring : BufferSize 87.5 GB, nChannels 1
// NCCL INFO Algorithm : NCCL_ALGO_RING
1
2
3
4
5
6
7
8
9
10
11
12
13
14

第5节:三种并行策略与通信开销 ​

数据并行(最常用) ​
┌─────────────────────────────────────────────────────────────┐
│                    数据并行通信开销                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  每迭代:                                                   │
│  1. Forward(各卡独立计算)                                │
│  2. Backward(各卡独立计算)                                │
│  3. AllReduce 梯度同步 ← 通信!                           │
│                                                             │
│  通信量:                                                  │
│  AllReduce(梯度) = 2 × 梯度大小 × (N-1) / N             │
│  175B FP16 = 350 GB                                      │
│  8 卡 AllReduce = 2 × 350GB × 7/8 = 612.5 GB           │
│                                                             │
│  假设迭代计算时间 = 1 秒,通信时间 = 0.5 秒               │
│  并行效率 = 1 / (1 + 0.5) = 67%                         │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
模型并行 ​
┌─────────────────────────────────────────────────────────────┐
│                    模型并行通信开销                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  模型按层切分到不同 GPU:                                  │
│  GPU0: Layer 1-24    GPU1: Layer 25-48                   │
│                                                             │
│  每迭代:                                                   │
│  1. GPU0 Forward → GPU1 Forward(激活值传递)← 通信    │
│  2. GPU1 Backward → GPU0 Backward(梯度传递)← 通信    │
│                                                             │
│  通信量:                                                  │
│  激活值大小 = batch_size × seq_len × hidden_dim × 2B   │
│  假设 seq_len=2048, hidden=12288, batch=8               │
│  激活值 = 8 × 2048 × 12288 × 2B = 402 MB              │
│  每迭代通信 ≈ 800 MB(比数据并行小很多!)                │
│                                                             │
│  缺点:                                                     │
│  跨 GPU 依赖强,GPU 利用率可能不高                        │
│  需要仔细的调度和流水线                                    │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
流水线并行 ​
┌─────────────────────────────────────────────────────────────┐
│                    流水线并行                                │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  把模型分成多个 Stage,形成流水线:                        │
│                                                             │
│  Stage 0: GPU0 (Embedding + Transformer 1-12)             │
│  Stage 1: GPU1 (Transformer 13-24)                       │
│  Stage 2: GPU2 (Transformer 25-36)                       │
│  Stage 3: GPU3 (Transformer 37-48 + Output)              │
│                                                             │
│  流水线示意(4 个 Micro-batch):                          │
│                                                             │
│  Time →                                                    │
│  GPU0: [F0][F1][F2][F3][B4][B3][B2][B1]                │
│  GPU1:     [W][F0][F1][F2][B4][B3][B2][B1]             │
│  GPU2:         [W][F0][F1][F2][B4][B3][B2][B1]         │
│  GPU3:             [W][F0][F1][F2][B4][B3][B2][B1]     │
│                                                             │
│  F=Forward, B=Backward, W=流水线气泡                     │
│                                                             │
│  优点:减少流水线气泡,提高 GPU 利用率                    │
│  缺点:需要仔细调度,否则效率低                          │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

升华:多卡训练的核心矛盾 ​

┌─────────────────────────────────────────────────────────────┐
│                    性能 vs 显存 trade-off                    │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  数据并行:                                                │
│  - 显存占用大(需要全模型)                               │
│  - 通信量大(AllReduce 梯度同步)                         │
│  - 简单易用,效果好                                        │
│                                                             │
│  模型并行:                                                │
│  - 显存占用小(分片)                                     │
│  - 通信量小(激活值传递)                                 │
│  - 难以负载均衡,效率可能低                               │
│                                                             │
│  流水线并行:                                              │
│  - 显存占用适中                                           │
│  - 需要大批量才能高效                                     │
│                                                             │
│  实际 175B 模型训练:                                    │
│  = 流水线并行(分 8 个 Stage)                            │
│  + 数据并行(每个 Stage 内 8 卡)                        │
│  = 64 卡并行(8 Stage × 8 数据并行)                    │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

"AI 可查 vs 必须理解"清单 ​

AI 可查:
✅ MPI_Send/MPI_Recv 的具体参数
✅ NCCL 的具体 API(torch.distributed)
✅ Ring AllReduce 的具体实现代码

必须理解:
🔴 MPI 点对点通信 vs 集合通信(AllReduce/Broadcast)
🔴 Ring AllReduce 原理:为什么比 Tree AllReduce 更适合 HPC
🔴 NCCL vs MPI:GPU 显存直接通信 vs CPU 内存通信
🔴 NVLink vs PCIe vs InfiniBand 的带宽差异
🔴 数据并行通信量(AllReduce 梯度)vs 模型并行通信量(激活值)
1
2
3
4
5
6
7
8
9
10
11

学习状态:🟡 开始学习


完整迁入:原分布式并行全文 ​

分布式并行:MPI、集合通信、RDMA 与多机多卡 / Distributed Parallelism with MPI, Collective Communication, and RDMA ​

📅 创建时间:2026-07-20 🏷️ 标签:#MPI #分布式计算 #集合通信 #RDMA #多机多卡 📚 前置知识:[[08-heterogeneous-computing]] 📚 相关知识:[[/02-systems-and-performance/02-computer-architecture-and-hardware/08-mpi-cluster-hpc]] [[/04-ai/03-ai-infrastructure/clusters/07-nccl-cluster-networking]]


1. 从共享内存到消息传递 ​

一台服务器中的线程可以访问共享内存。不同服务器拥有独立地址空间,只能通过网络交换消息。

text
节点 A                         节点 B
CPU + 内存 A   ← 网络消息 →    CPU + 内存 B
GPU 0/1                       GPU 0/1
1
2
3

程序必须明确:谁拥有数据、何时发送、发给谁、接收后怎样继续计算。


2. MPI 的基本概念 ​

MPI 是高性能计算中常用的消息传递标准。每个进程拥有 Rank:

text
Rank 0  Rank 1  Rank 2  Rank 3
1

点对点通信:

cpp
MPI_Send(..., destination, tag, communicator);
MPI_Recv(..., source, tag, communicator, ...);
1
2

需要避免双方都等待接收、消息标签不匹配和缓冲区生命周期错误。


3. 集合通信 ​

Broadcast ​

一个 Rank 把数据发给所有 Rank。

Reduce ​

各 Rank 的数据进行求和、最大值等归约,结果送到一个 Rank。

AllReduce ​

所有 Rank 提供数据,并让所有 Rank 获得归约结果。分布式训练的梯度同步经常使用它。

AllGather ​

每个 Rank 收集所有 Rank 的数据分片。

AllToAll ​

每个 Rank 向所有其他 Rank 发送不同数据,常见于专家并行和数据重分布。

成熟通信库会根据消息大小和拓扑选择 Ring、Tree 等算法。


4. 延迟与带宽模型 ​

一次消息传输可以粗略表示为:

text
通信时间 = 启动延迟 α + 数据量 / 带宽
1

大量小消息主要受延迟影响,大消息主要受带宽影响。因此常见优化包括:

  • 合并小消息
  • 减少同步次数
  • 使用非阻塞通信
  • 在计算期间提前发送边界数据
  • 让通信模式匹配网络拓扑

5. Halo Exchange ​

网格被划分到不同节点后,每个节点需要邻居的边界数据:

text
节点 A 区域 | Halo | 节点 B 区域
1

典型步骤:

  1. 更新本地内部网格。
  2. 异步发送边界。
  3. 同时计算不依赖远程数据的区域。
  4. 等待边界到达。
  5. 计算边界区域。

这是通信与计算重叠的经典案例。


6. RDMA 与高速互联 ​

传统网络通信需要多次内核和内存拷贝。RDMA 允许网卡直接访问已注册内存,减少 CPU 参与和拷贝。

集群常见互联层次:

text
GPU 内部互联 / NVLink
节点内 PCIe / NVSwitch
节点间 InfiniBand / RoCE / Ethernet
1
2
3

通信库需要理解拓扑,避免本可走高速链路的数据绕行低速路径。


7. 多 GPU 并行策略 ​

数据并行 ​

每张 GPU 保留完整模型,处理不同数据,最后同步梯度。简单,但模型必须能放入单卡。

张量并行 ​

把单个矩阵或层切到多张 GPU,需要频繁集合通信。

流水线并行 ​

不同 GPU 保存不同层,微批次在阶段间流动,存在流水线气泡。

专家并行 ​

不同专家分布在不同设备,Token 通过 AllToAll 路由,负载均衡十分重要。

现实中的大型训练常组合多种并行方式。


8. 分布式系统特有问题 ​

  • 某个 Rank 变慢会拖慢全部同步参与者
  • 网络拥塞造成性能抖动
  • 节点或 GPU 故障导致任务中止
  • 日志分散,定位问题困难
  • 不同节点环境不一致
  • Checkpoint 规模大、写入慢

高性能集群不仅是算法问题,也依赖调度、监控、存储和运维能力。


核心总结 ​

  • 分布式并行使用消息而不是共享变量协作。
  • 通信成本由启动延迟、带宽、数据量和拓扑共同决定。
  • 集合通信是多机 AI 与 HPC 的关键基础设施。
  • 应尽量重叠通信和计算,并减少全局同步。
  • 扩展性能最终常被最慢节点和网络限制。

下一篇:[[10-performance-engineering]] <!-- migrated-deep-dive:end -->

面试速答 ​

MPI 与 OpenMP 区别? MPI 用显式消息连接独立进程,可跨节点;OpenMP 主要面向共享内存线程。二者可以混合。

阻塞发送是否一定死锁? 不一定,但相互等待且没有可匹配接收时可能死锁;不能依赖实现内部缓冲碰巧足够。

强扩展与弱扩展? 强扩展固定总问题规模增加资源;弱扩展按资源同步增加问题规模,观察每份工作时间是否稳定。

自测 ​

  1. Rank 0 是否一定运行在第一个 CPU 核心?
  2. 非阻塞通信是否意味着可立刻复用发送缓冲区?
  3. 为什么增加节点可能变慢?

答案:不一定;不能,完成前缓冲区仍受约束;局部工作变少而通信、同步与负载不均占比上升。

最后更新于:

Pager
上一篇11. CPU-GPU 异构流水线
下一篇13. 并行算法模式

持续记录,持续成长

Copyright © Tidenflow