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

本页目录

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

“并行”不是一种东西,而是发生在不同层次的工作重叠。先辨认层次,再选择工具。

text
one problem
├── one instruction stream
│   ├── ILP: several independent instructions overlap inside a CPU core
│   └── SIMD: one instruction operates on several data lanes
├── several threads: usually shared-memory CPU parallelism
├── many GPU threads: SIMT execution on warps and SMs
└── several processes: separate address spaces, messages, possibly many nodes
1
2
3
4
5
6
7

1. 串行、并发、并行、异步 ​

  • 串行:前一项完成后才开始下一项。
  • 并发:多个任务生命周期重叠,单核也能靠切换实现。
  • 并行:多个任务在同一时刻由不同执行资源推进。
  • 异步:发起操作后不必原地等待,描述控制流而非硬件数量。

异步不必然并行,并发也不必然多核。

2. 三种 CPU 并行层次 ​

ILP:指令级并行 ​

同一线程中的独立指令可由流水线、超标量和乱序执行重叠。主要由处理器和编译器完成。

SIMD:数据级并行 ​

一条 AVX/NEON 等向量指令操作多个数据元素。适合循环中同构、独立的计算。

线程级并行 ​

把任务分给多个软件线程,并由多个核心真正并行执行。工具包括 std::thread、线程池和 OpenMP。

三者可以同时存在:8 个 CPU 线程中的每个线程都可能乱序执行并使用 SIMD。

3. GPU SIMT ​

CUDA 用大量逻辑线程描述工作,硬件把它们组织成 Warp 并映射到 SM。SIMT 让每个线程拥有索引和寄存器状态,但同一 Warp 的控制流越一致,利用率通常越高。

4. 进程与分布式执行 ​

线程通常共享地址空间;进程拥有独立地址空间。MPI 通过显式消息让多个进程合作,它们可以在同一机器,也可以跨节点。

text
node 0 memory <-- network --> node 1 memory
  rank 0                       rank 1

no ordinary shared pointer crosses this boundary
1
2
3
4

5. 怎样选择 ​

问题特征首先考虑
小规模、强依赖、控制复杂优化串行与算法
连续数组的相同操作自动向量化 / SIMD
单机共享数据的粗粒度任务线程池 / OpenMP
海量规则数据并行GPU / CUDA
数据超过单机或需要多节点MPI / 分布式系统

并行化前先问:工作能否独立?数据在哪里?同步多少?粒度是否大到足以覆盖调度与通信成本?

深入原理与工程实践 ​

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

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

完整迁入:原并行计算理论全文 ​

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

📅 创建时间:2026-06-02 🏷️ 标签:#HPC #并行计算 #阿姆达尔定律 #Flynn分类 #加速比 📚 前置知识:[[01-computer-architecture-basics]](计算机架构基础) 📚 相关知识:[[03-gpu-architecture]](GPU 架构) [[08-mpi-cluster-hpc]](多卡并行)


先抓住直觉 ​

十个人一起搬家,不会必然快十倍:有些物品只能由一个人处理,大家还要分工、沟通和等待。并行计算研究的就是“能同时做多少”以及“协作成本有多大”。

  • 必须理解:可并行部分、串行部分、加速比和并行效率。
  • 用到再查:公式推导、Flynn 分类名称和具体并行框架。
  • 读公式前先问:增加资源后,剩下的时间究竟花在串行工作还是通信上?

场景:你的 30 天训练,理论极限是多少? ​

┌─────────────────────────────────────────────────────────────┐
│                                                             │
│  当前:单卡 A100 训练 175B 参数模型                        │
│  预计时间:30 天                                            │
│                                                             │
│  你决定用 8 卡并行训练。                                   │
│  8 倍快?那就是 30/8 = 3.75 天?                          │
│                                                             │
│  等等。                                                    │
│  同事用 8 卡,跑了 5 天,还是没跑完。                     │
│  理论加速比不是 8 倍吗?瓶颈在哪里?                       │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13

这一章,我们用数学工具回答这个问题:并行化的理论极限在哪里?


第1节:串行 vs 并行——最基本的区别 ​

串行计算 ​
你一个人搬家:
  第1步:把书装进箱子
  第2步:把箱子搬上车
  第3步:开车到新家
  第4步:把箱子搬进屋
  → 一个人,顺序执行,每一步必须等上一步完成
1
2
3
4
5
6
并行计算 ​
8 个人一起搬家:
  第1步:每个人负责不同的房间(同时装书)
  第2步:每个人搬自己负责的房间(同时搬运)
  → 8 个人可以同时工作
1
2
3
4
用时间衡量 ​
串行时间 T(1) = t1 + t2 + t3 + t4
并行时间 T(N) = max(t1, t2, t3, t4) / N + 同步开销 + 通信开销
1
2

第2节:阿姆达尔定律——并行加速的数学天花板 ​

定律内容 ​
┌─────────────────────────────────────────────────────────────┐
│                    阿姆达尔定律(Amdahl's Law)               │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  任意程序的加速比由它的串行部分决定。                        │
│                                                             │
│  Speedup(N) = 1 / (S + P/N)                               │
│                                                             │
│  其中:                                                     │
│    S = 程序的串行部分占比(Serial fraction)               │
│    P = 程序的并行部分占比(Parallel fraction)              │
│    N = 并行 worker 数量                                    │
│    S + P = 1                                               │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
推到过程 ​
程序的执行时间:
T(1) = S × T(1) + P × T(1)          // 串行部分 + 并行部分(单核)
T(N) = S × T(1) + (P × T(1)) / N    // 串行不变,并行按 N 加速

加速比:
Speedup = T(1) / T(N)
        = T(1) / [S × T(1) + (P × T(1))/N]
        = 1 / [S + P/N]
1
2
3
4
5
6
7
8
数值例子 ​
假设:程序有 10% 是串行的(S = 0.1),90% 可并行(P = 0.9)

Speedup(1)  = 1 / (0.1 + 0.9/1)  = 1.00   (无加速)
Speedup(2)  = 1 / (0.1 + 0.9/2)  = 1.82   (不是 2!)
Speedup(4)  = 1 / (0.1 + 0.9/4)  = 3.08
Speedup(8)  = 1 / (0.1 + 0.9/8)  = 4.71
Speedup(16) = 1 / (0.1 + 0.9/16) = 6.40
Speedup(∞)  = 1 / 0.1              = 10     (理论极限!)

关键发现:
即使你有无穷多个核心,这个程序最多只能快 10 倍。
因为那 10% 的串行部分是无法并行的!
1
2
3
4
5
6
7
8
9
10
11
12
图解:加速比曲线 ​
┌─────────────────────────────────────────────────────────────┐
│              阿姆达尔定律:加速比随核心数的变化                  │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  加速比                                                        │
│     ↑                                                         │
│  10 ┤─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─(理论极限 10x)  │
│     │      ╭''''''''''\                                    │
│   8 ┤─ ─ ─╱                \                                │
│     │     ╱                  \                              │
│   6 ┤─ ─╱                    \                              │
│     │   ╱                      \                            │
│   4 ┤─ ╱                        \                          │
│     │  ╱                          \                        │
│   2 ┤─╱                            \                        │
│     │ ╱                               \                     │
│   1 ┼──────────────────────────────────────────────────→   │
│         2     4     8    16    32    64    核心数 N        │
│                                                             │
│  串行比例 S=0.1(10%)的加速比曲线                           │
│  曲线越来越平,接近 10 的理论极限                             │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
不同串行比例的影响 ​
┌─────────────────────────────────────────────────────────────┐
│              不同串行比例下的理论加速比                        │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  串行比例   │  2核   │  4核   │  8核   │  16核  │ 理论极限 │
│  ──────────┼────────┼────────┼────────┼────────┼─────────┤
│  S = 5%   │  1.90  │  3.48  │  5.93  │  9.38  │  20x    │
│  S = 10%  │  1.82  │  3.08  │  4.71  │  6.40  │  10x    │
│  S = 20%  │  1.67  │  2.67  │  3.48  │  4.44  │   5x    │
│  S = 50%  │  1.33  │  1.60  │  1.78  │  1.94  │   2x    │
│  S = 90%  │  1.11  │  1.19  │  1.25  │  1.31  │  1.1x   │
│                                                             │
│  结论:串行比例越高,增加核心数的收益越小。                     │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

第3节:回到训练场景——你的 30 天能优化到多久? ​

训练过程的时间分解 ​
┌─────────────────────────────────────────────────────────────┐
│           大模型训练时间分解(单卡 A100)                      │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  100% = Forward + Backward + Optimizer + Communication      │
│                                                             │
│  Forward(矩阵运算,并行)      ≈ 40%                        │
│  Backward(梯度计算,并行)    ≈ 40%                        │
│  Optimizer(Adam 更新,串行)  ≈ 10%  ← 串行部分!         │
│  Communication(梯度同步)      ≈ 10%  ← 通信开销!         │
│                                                             │
│  实际串行比例 S ≈ 20%(考虑通信开销)                        │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
8 卡并行能快多少? ​
S = 0.2(20% 串行 + 通信)
P = 0.8(80% 可并行)

Speedup(8) = 1 / (0.2 + 0.8/8)
           = 1 / (0.2 + 0.1)
           = 1 / 0.3
           = 3.33x

30 天 / 3.33 = 9 天

但实际上你的同事跑了 5 天!为什么比理论还快?
→ 因为通信优化做得好(NVLink),实际 S 更小
→ 或者用了更好的并行策略
1
2
3
4
5
6
7
8
9
10
11
12
13
为什么实际可能比理论快?(Gustafson 定律) ​

阿姆达尔定律假设问题规模固定,但实际训练中:

  • 数据集大小是固定的
  • 模型大小是固定的
  • 但绝对通信时间可能比理论假设的小

Gustafson 定律(弱扩展视角):

Scaled Speedup = N + S × (1 - N)

假设 S = 10%(通信和同步开销 10%)
8 卡加速比 = 8 + 0.1 × (1 - 8) = 8 - 0.7 = 7.3x(接近理想的 8x)

这说明:当问题规模随核心数增加时,加速比更接近线性。
1
2
3
4
5
6

第4节:Flynn 分类——并行计算的四大门派 ​

四种计算模型 ​
┌─────────────────────────────────────────────────────────────┐
│                      Flynn 分类法                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│                    指令      数据                             │
│                      ↓        ↓                             │
│                  ┌────────┬────────┐                        │
│                  │   单条   │  单条   │ → SISD              │
│                  │  指令   │  数据   │   (传统 CPU)        │
│                  ├────────┼────────┤                        │
│                  │   单条   │  多条   │ → SIMD              │
│                  │  指令   │  数据   │   (GPU/向量机)       │
│                  ├────────┼────────┤                        │
│                  │   多条   │  单条   │ → MISD              │
│                  │  指令   │  数据   │   (少用)             │
│                  ├────────┼────────┤                        │
│                  │   多条   │  多条   │ → MIMD              │
│                  │  指令   │  数据   │   (集群/分布式)      │
│                  └────────┴────────┘                        │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
每种类型的代表 ​
┌─────────────────────────────────────────────────────────────┐
│                    Flynn 分类详解                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  SISD = Single Instruction Single Data                       │
│  ┌───────────────────────────────────────────────────────┐  │
│  │  CPU:一条指令处理一个数据                            │  │
│  │  for (i=0; i<N; i++) sum += a[i];                  │  │
│  │  → 串行执行,循环 N 次                               │  │
│  └───────────────────────────────────────────────────────┘  │
│                                                             │
│  SIMD = Single Instruction Multiple Data                     │
│  ┌───────────────────────────────────────────────────────┐  │
│  │  GPU:一条指令同时处理 N 个数据(一个 Warp = 32 个线程)│  │
│  │  c = a + b  (同时做 N 次加法)                      │  │
│  │  → 一次执行,N 个 ALU 同时运算                        │  │
│  └───────────────────────────────────────────────────────┘  │
│                                                             │
│  MISD = Multiple Instruction Single Data                    │
│  ┌───────────────────────────────────────────────────────┐  │
│  │  很少见:同一数据被多个指令处理                        │  │
│  │  典型场景:飞机的多个传感器对同一数据进行冗余计算       │  │
│  └───────────────────────────────────────────────────────┘  │
│                                                             │
│  MIMD = Multiple Instruction Multiple Data                   │
│  ┌───────────────────────────────────────────────────────┐  │
│  │  多节点集群:每个节点独立执行不同指令处理不同数据       │  │
│  │  Node0: 处理 Batch0    Node1: 处理 Batch1            │  │
│  │  → 数据并行训练就是 MIMD 的典型应用                    │  │
│  └───────────────────────────────────────────────────────┘  │
│                                                             │
└─────────────────────────────────────────────────────────────┘
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
与训练场景的对应 ​
AI 训练的三层并行:
  Layer 1(GPU 内部)   → SIMD:GPU 一个 Warp 同时处理 32 个数据
  Layer 2(单节点多卡)→ MIMD:每个 GPU 独立处理不同的数据批次
  Layer 3(多节点)    → MIMD:每个计算节点独立运行,通过 MPI 同步

三层叠加:
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│   Node 0                Node 1                Node 2        │
│  ┌────────┐          ┌────────┐          ┌────────┐        │
│  │ GPU 0  │          │ GPU 0  │          │ GPU 0  │        │
│  │ GPU 1  │          │ GPU 1  │          │ GPU 1  │        │
│  │ GPU 2  │          │ GPU 2  │          │ GPU 2  │        │
│  │ GPU 3  │          │ GPU 3  │          │ GPU 3  │        │
│  └────────┘          └────────┘          └────────┘        │
│       ↑                   ↑                   ↑            │
│   NVLink              NVLink               NVLink            │
│       ↑                   ↑                   ↑            │
│   ┌────┴───────────────────┴───────────────────┴────┐    │
│   │            InfiniBand / RoCE 网络                  │    │
│   └────────────────────────────────────────────────────┘    │
│                                                             │
│   Layer 1:GPU Warp SIMD(单指令多数据)                    │
│   Layer 2:同节点多 GPU MIMD(通过 NVLink                  │
│   Layer 3:多节点 MIMD(通过 InfiniBand)                   │
│                                                             │
└─────────────────────────────────────────────────────────────┘
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

第5节:并行效率——8 卡真的用了 8 倍算力吗? ​

并行效率公式 ​
并行效率 E(N) = 实际加速比 / 理论加速比 = Speedup(N) / N

理想情况:E(N) = 1(100%,8 卡快 8 倍)
实际情况:E(N) < 1(因为有通信、同步、负载不均等开销)
1
2
3
4
常见效率陷阱 ​
┌─────────────────────────────────────────────────────────────┐
│                   并行效率损失的原因                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. 通信开销                                               │
│     每个迭代结束后,所有 GPU 必须同步梯度                     │
│     梯度大小 = 模型参数 × 4 字节(FP32)                   │
│     175B 参数 = 700 GB → 8 卡各传 700 GB?                │
│     实际上通过 AllReduce 优化为 8 份 × 1 份传输             │
│                                                             │
│  2. 负载不均衡                                              │
│     不同层的计算量不同,某些 GPU 先完成等待其他 GPU          │
│                                                             │
│  3. 资源争抢                                                │
│     多卡共享 PCIe/NVLink 带宽                               │
│                                                             │
│  4. 启动开销                                                │
│     GPU kernel 启动有固定开销(小任务可能不值得并行)        │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
估算 8 卡训练的效率 ​
实际场景:
  - 梯度同步(AllReduce):通信时间 ≈ 计算时间的 10-20%
  - 负载不均:5%
  - 同步开销:5%

  实际并行效率 ≈ 70-80%

  8 卡加速比 ≈ 8 × 0.75 = 6x

30 天 / 6 = 5 天

这就解释了为什么你的同事 5 天跑完——实际效率约 75%!
1
2
3
4
5
6
7
8
9
10
11
12

第6节:如何提高并行效率——三种并行策略 ​

数据并行(Data Parallelism) ​
┌─────────────────────────────────────────────────────────────┐
│                    数据并行示意                               │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1份模型参数(复制到每张 GPU)                              │
│                                                             │
│  GPU 0 ← 处理 Batch 0-999                                 │
│  GPU 1 ← 处理 Batch 1000-1999                             │
│  GPU 2 ← 处理 Batch 2000-2999                             │
│  GPU 3 ← 处理 Batch 3000-3999                             │
│  ...                                                       │
│                                                             │
│  每轮迭代结束后:AllReduce 汇总所有 GPU 的梯度               │
│                                                             │
│  优点:简单,实现容易                                       │
│  缺点:所有 GPU 必须持有完整模型(显存压力大)               │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
模型并行(Model Parallelism) ​
┌─────────────────────────────────────────────────────────────┐
│                    模型并行示意                               │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  模型按层切分,每张 GPU 持有部分层                           │
│                                                             │
│  Input                                                     │
│    ↓                                                        │
│  GPU 0: Layer 1-24(Embedding + Transformer 1-12)         │
│    ↓                                                        │
│  GPU 1: Layer 25-48(Transformer 13-24 + Output)          │
│    ↓                                                        │
│  Output                                                    │
│                                                             │
│  前向传播:GPU 0 → GPU 1(中间激活值传递)                  │
│  反向传播:GPU 1 → GPU 0(梯度传回)                       │
│                                                             │
│  优点:突破单卡显存限制,支持超大模型                       │
│  缺点:跨 GPU 通信量大,效率低(跨节点更严重)              │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
流水线并行(Pipeline Parallelism) ​
┌─────────────────────────────────────────────────────────────┐
│                    流水线并行示意                             │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  把模型并行分成多个阶段,形成流水线                          │
│                                                             │
│  Stage 0      Stage 1      Stage 2      Stage 3           │
│  ┌───────┐   ┌───────┐   ┌───────┐   ┌───────┐          │
│  │GPU 0  │   │GPU 1  │   │GPU 2  │   │GPU 3  │          │
│  │L1-L12 │   │L13-L24│   │L25-L36│   │L37-L48│          │
│  └───────┘   └───────┘   └───────┘   └───────┘          │
│       ↓          ↓          ↓          ↓                  │
│    F0/B3      F1/B2      F2/B1      F3/B0               │
│                                                             │
│  F=Forward, B=Backward                                     │
│  同一时刻每张 GPU 做不同的事情(流水线)                    │
│                                                             │
│  优点:减少流水线气泡,提高 GPU 利用率                      │
│  缺点:需要仔细调度,否则气泡多                               │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
三种并行策略对比 ​
┌─────────────────────────────────────────────────────────────┐
│                三种并行策略对比                               │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  策略        │ 通信模式        │ 显存占用    │ 通信量      │
│  ────────────┼────────────────┼────────────┼─────────────  │
│  数据并行    │ AllReduce(梯度)│ 全模型×N  │ 中等         │
│  模型并行    │ 点对点(激活值)│ 全数据×N  │ 大(激活值) │
│  流水线并行  │ 点对点(阶段间)│ 全数据×N  │ 中等         │
│                                                             │
│  实际部署(千亿参数模型):                                   │
│  → 8×A100(80GB)单卡能装 70B 参数(FP16)                 │
│  → 175B 需要模型并行 + 流水线并行 + 数据并行                │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

升华:30 天优化到 3 天,需要什么? ​

┌─────────────────────────────────────────────────────────────┐
│                  加速路径推演                                │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  10x 加速从哪里来?                                         │
│                                                             │
│  因素 1:GPU 架构(vs CPU)         → ~20x 加速          │
│  因素 2:混合精度(FP32 → FP16)    → ~2x 加速           │
│  因素 3:8 卡数据并行               → ~6x 加速           │
│  因素 4:梯度累积(模拟更大 batch) → ~1.5x 加速         │
│  因素 5:Activation Checkpointing   → ~1.5x 加速         │
│  因素 6:通信优化(NVLink)         → ~1.2x 加速         │
│                                                             │
│  综合效果:20 × 2 × 6 × 1.5 × 1.5 × 1.2 ≈ 648x?       │
│  实际因为阿姆达尔定律和通信开销,远低于理论乘积               │
│  实际效果:单卡 30 天 → 多卡 3 天(~10x)                   │
│                                                             │
│  关键:不是某一个优化,而是多个优化叠加                      │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

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

AI 可查:
✅ NVLink、InfiniBand 的具体带宽数值——官网查
✅ AllReduce 的具体实现算法——Ring AllReduce vs Tree AllReduce
✅ 各种并行框架的具体配置参数——DeepSpeed/Megatron 文档

必须理解:
🔴 阿姆达尔定律公式:Speedup = 1 / (S + P/N)
🔴 串行比例 S 决定理论加速上限
🔴 Flynn 分类:SISD/SIMD/MISD/MIMD
🔴 SIMD = GPU 的核心执行模型
🔴 数据并行 vs 模型并行 vs 流水线并行的 trade-off
🔴 并行效率 = 实际加速比 / N(为什么 E < 1)
1
2
3
4
5
6
7
8
9
10
11
12

学习状态:🟡 开始学习


完整迁入:原并行基础:分解、加速比与扩展性 ​

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

📅 创建时间:2026-07-20 🏷️ 标签:#并行计算 #Amdahl定律 #Gustafson定律 #性能模型 📚 前置知识:[[00-parallel-computing-overview]] 📚 相关知识:[[/02-systems-and-performance/02-computer-architecture-and-hardware/02-parallel-computing-theory]]


1. 并行之前先判断“能否拆分” ​

假设程序由以下步骤组成:

text
读取数据 → 预处理 → 核心计算 → 汇总结果 → 写入文件
1

只有相互独立的部分能够同时执行。任务之间常见依赖包括:

  • 数据依赖:后一步需要前一步的结果
  • 控制依赖:是否执行由前一步判断决定
  • 资源依赖:多个任务竞争同一文件、锁或设备
数据并行与任务并行 ​
text
数据并行:同一种操作处理不同数据
例:多个线程分别处理图像的不同行

任务并行:不同任务同时处理同一流程的不同阶段
例:一个线程读取、一个线程解码、一个线程推理
1
2
3
4
5

GPU 主要擅长数据并行,CPU 更容易同时承载数据并行和任务并行。


2. 衡量并行性能 ​

加速比 ​
text
S(p) = T(1) / T(p)
1

如果单线程需要 100 秒,8 线程需要 20 秒,则加速比为 5,而不是 8。

并行效率 ​
text
E(p) = S(p) / p
1

上例的并行效率是 5 / 8 = 62.5%。剩余能力消耗在串行部分、同步、调度和负载不均上。

吞吐与延迟 ​
  • 延迟:完成单个任务需要多久
  • 吞吐:单位时间能完成多少任务

GPU 往往提高吞吐,但单个小任务不一定比 CPU 延迟更低。


3. Amdahl 定律:固定问题规模的上限 ​

如果程序中可并行比例为 P,使用 N 个处理器:

text
S(N) = 1 / ((1 - P) + P / N)
1

当 95% 可以并行时,即使处理器无限多:

text
最大加速比 = 1 / (1 - 0.95) = 20
1

这说明优化串行路径可能比继续增加核心更重要。

可并行比例理论最大加速比
50%2 倍
90%10 倍
95%20 倍
99%100 倍

4. Gustafson 定律:问题规模也会增长 ​

现实中获得更多计算资源后,通常不是只想更快完成原问题,而是希望处理更大的网格、更高分辨率或更大的模型。

text
S(N) = N - α(N - 1)
1

其中 α 是串行部分比例。Gustafson 定律解释了为什么超级计算机仍然有价值:资源增加后,可以扩大并行工作规模。


5. 强扩展与弱扩展 ​

强扩展 ​

保持总问题规模不变,增加处理器数量。

text
固定 1 亿个网格单元:1 核 → 8 核 → 64 核
1

处理器越多,每个处理器分到的工作越少,通信占比最终会上升。

弱扩展 ​

每个处理器的工作量保持不变,处理器增加时同步扩大问题规模。

text
每核处理 100 万个网格单元:1 核 100 万 → 64 核 6400 万
1

弱扩展更能反映大型科学计算系统的容量能力。


6. 粒度、调度与负载均衡 ​

并行粒度 ​
  • 粗粒度:任务大、调度少,但可能不均衡
  • 细粒度:任务均衡机会多,但调度和同步成本高

任务耗时只有几百纳秒时,放进线程池可能比直接执行更慢。

静态调度与动态调度 ​
  • 静态调度:提前分工,开销低,适合任务耗时相近
  • 动态调度:运行时领取任务,均衡好,但有额外竞争
负载不均衡 ​
text
线程 A:100 ms
线程 B:102 ms
线程 C:98 ms
线程 D:300 ms  ← 所有人都要等它
1
2
3
4

并行阶段耗时由最慢参与者决定。


7. 同步不是免费的 ​

互斥锁、原子操作、Barrier 和消息通信都会带来等待。常见优化方向:

  • 减少共享可变状态
  • 批量处理,降低同步频率
  • 使用线程局部数据,最后统一归并
  • 让通信与计算重叠
  • 通过无锁结构降低阻塞,但不要默认无锁一定更快

章节测试 ​

  1. 为什么 16 核程序通常无法获得 16 倍加速?
  2. 图像逐像素处理更接近数据并行还是任务并行?
  3. 固定网格规模增加节点属于强扩展还是弱扩展?
  4. 为什么任务拆得过细也会降低性能?

参考答案 ​

  1. 存在串行部分、同步、调度、访存和负载不均。
  2. 数据并行。
  3. 强扩展。
  4. 调度、通信和同步成本可能超过任务本身。

下一篇:[[02-processor-architecture]] <!-- migrated-deep-dive:end -->

面试速答 ​

普通 C++ 编译会自动使用多核吗? 一般不会自动把任意串行算法变成多线程;编译器会做 ILP 调度和可能的自动向量化,多核通常需要显式线程或并行框架。

线程和核心是什么关系? 线程是软件执行流,核心是硬件资源。可运行线程由 OS 调度到逻辑 CPU,一个核心还可能提供多个硬件线程。

自测 ​

  1. async API 是否证明任务正在另一个核心执行?
  2. OpenMP 和 AVX 为什么可以叠加?
  3. MPI 是否只能用于多机?

答案:否;它们分别使用线程级与数据级并行;否,MPI 进程也可位于同一台机器。

最后更新于:

Pager
上一篇5. GPU、SM、Warp 与显存
下一篇7. SIMD 与编译器向量化

持续记录,持续成长

Copyright © Tidenflow