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

本页目录

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

不要为每种技术换一个完全不同的 Demo。选一个可验证、可分块的任务,逐层扩展,才能真正比较执行模型。

推荐主线:对大数组完成 y[i] = a*x[i] + y[i],再扩展到归约和二维 Stencil。

阶段 0:正确性与标量基线 ​

  • 写清输入规模、数据类型和误差容忍度;
  • 生成固定随机种子输入;
  • 保存简单标量参考实现;
  • 使用 Release 优化构建,测量多轮中位数;
  • 报告处理字节数、时间和有效带宽。

交付物:结果校验、基线表格、构建命令。没有这一步,后面无法判断并行版本是否正确、是否更快。

阶段 1:编译器与 SIMD ​

  1. 查看编译器向量化报告;
  2. 检查别名、循环依赖和数据布局;
  3. 比较禁用/启用向量化;
  4. 仅在证据表明需要时尝试 intrinsics;
  5. 记录小输入与大输入差异。

应回答:程序是计算受限还是带宽受限?向量化后指令减少,为何速度未必按 lane 数增长?

阶段 2:std::thread 或 OpenMP ​

把数组切成连续区间,每线程处理一个区间:

text
thread 0: [0 ........ n/4)
thread 1: [n/4 ...... n/2)
thread 2: [n/2 ...... 3n/4)
thread 3: [3n/4 ..... n)
1
2
3
4

测试 1、2、4、8…线程,计算 speedup 和 efficiency。增加一个归约任务,对比全局锁、原子和线程局部结果三种方案。观察内存带宽何时饱和。

阶段 3:CUDA ​

  • 写一线程一元素 Kernel,并处理边界;
  • 分别测 H2D、Kernel、D2H 和端到端时间;
  • 扫描若干合理 Block 大小,不宣称某个数永远最佳;
  • 用 profiler 检查合并访存、带宽和 Occupancy;
  • 批处理多个操作,使数据留在 GPU;
  • 与成熟库或 CPU 优化基线比较。

只有 NVIDIA GPU、兼容驱动与 Toolkit 准备好时才运行 CUDA 实验;缺少环境时保留源码与预期观察,不伪造测量结果。

阶段 4:MPI ​

每个 Rank 处理数组片段并通过 MPI_Reduce 合并结果。随后实现二维 Stencil:分解网格、交换 Halo、计算内部与边界。

分别测试:

  • 同一机器多个 Rank;
  • 条件允许时多个节点;
  • 阻塞通信基线;
  • 非阻塞通信与内部计算重叠;
  • 强扩展与弱扩展。

阶段 5:混合模型 ​

可选组合:每节点一个或多个 MPI Rank;Rank 内 OpenMP 使用 CPU 核;或每 Rank 管理一个 GPU。明确进程、CPU 核、NUMA 节点和 GPU 的绑定关系。

text
node 0                         node 1
MPI rank 0 -- GPU 0            MPI rank 1 -- GPU 1
     | OpenMP threads               | OpenMP threads
     +---------- network -----------+
1
2
3
4

统一实验记录模板 ​

字段内容
版本标量 / SIMD / OpenMP / CUDA / MPI
硬件与软件CPU/GPU、编译器、关键参数
输入规模、类型、分布
正确性参考值、误差、是否通过
资源线程、Block、Rank、节点数
时间分阶段与端到端
吞吐elements/s、GB/s 或 FLOP/s
结论瓶颈证据与下一假设

深入原理与工程实践 ​

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

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

完整迁入:原递进学习项目全文 ​

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

📅 创建时间:2026-07-20 🏷️ 标签:#学习路线 #并行项目 #CUDA实践 #MPI实践 📚 前置知识:[[11-application-cases]]


1. 学习目标 ​

并行计算不能只靠阅读。建议为每一层建立“串行基线、并行版本、性能证据和正确性测试”。

最终应该能够独立完成:

  • 设计可重复的 Benchmark
  • 使用多线程和 SIMD 优化 CPU 程序
  • 编写并分析 CUDA Kernel
  • 用 MPI 把问题扩展到多个进程
  • 解释性能没有线性扩展的原因
  • 输出包含环境、数据和结论的性能报告

2. 实验一:内存访问与 Cache ​

内容 ​
  • 比较连续访问、跨步访问和随机访问
  • 比较行优先与列优先矩阵遍历
  • 改变数组规模,观察超过不同 Cache 容量后的变化
记录 ​
  • 每秒处理字节数
  • Cache Miss
  • 不同步长的耗时曲线

目标:建立“计算量相同,数据访问不同,性能可以差很多”的直觉。


3. 实验二:CPU 多线程归约 ​

实现数组求和:

  1. 串行版本
  2. 每次加法使用全局锁的错误并行版本
  3. 每线程局部求和、最后归约
  4. OpenMP Reduction

比较 1、2、4、8、16 个线程的加速比和效率,并解释锁竞争与内存带宽上限。


4. 实验三:SIMD 与编译器向量化 ​

  • 编译同一循环的 Debug 和 Release 版本
  • 查看编译器向量化报告
  • 添加循环依赖让向量化失败
  • 调整数据对齐和数据布局
  • 比较标量、自动向量化和手写 Intrinsics

重点不是追求某个数字,而是能用报告证明编译器做了什么。


5. 实验四:CUDA 向量与矩阵运算 ​

第一阶段 ​
  • Vector Add
  • 边界判断
  • CUDA 错误检查
  • 使用 Event 测量 Kernel 时间
第二阶段 ​
  • 朴素矩阵乘法
  • Shared Memory 分块
  • 不同 Block 尺寸
  • 与 cuBLAS 比较

目标:理解自写 Kernel 与成熟库之间的差距来自哪些优化。


6. 实验五:并行算法原语 ​

依次实现:

  • Map
  • Reduce
  • Histogram
  • Prefix Scan
  • Stream Compaction

重点观察同步范围、原子操作、共享内存和多 Kernel 分阶段执行。


7. 实验六:CPU-GPU 流水线 ​

构建批量图像处理程序:

text
CPU 读取 → CPU 解码 → GPU 滤波 → CPU 编码
1

逐步加入:

  • Pinned Memory
  • 异步拷贝
  • 两个或多个 Stream
  • 双缓冲
  • 批处理

用时间线证明计算与传输是否真正重叠。


8. 实验七:MPI 网格计算 ​

实现二维热传导或简单 Jacobi 迭代:

  • 按行划分网格
  • 相邻 Rank 交换边界
  • 从阻塞通信改为非阻塞通信
  • 重叠内部区域计算与 Halo Exchange
  • 测试强扩展和弱扩展

记录每个 Rank 的计算、通信和等待时间。


9. 综合项目建议 ​

项目 A:异构图像处理引擎 ​

关键词:线程池、SIMD、CUDA、流水线、性能面板。

项目 B:并行有限差分求解器 ​

关键词:Stencil、OpenMP、CUDA、MPI、Halo Exchange、VTK 输出。

项目 C:小型分布式训练实验 ​

关键词:PyTorch DDP、NCCL、数据并行、梯度同步、Profiler。

项目 D:并行性能教学可视化 ​

关键词:Amdahl、Roofline、Cache、Warp 发散、通信拓扑。


10. 每个项目的交付物 ​

text
README
├─ 问题定义
├─ 硬件与软件环境
├─ 串行基线
├─ 并行设计
├─ 正确性验证
├─ Benchmark 方法
├─ 性能图表
├─ 瓶颈分析
└─ 已知限制与下一步
1
2
3
4
5
6
7
8
9
10

没有正确性测试的加速结果没有意义,没有测量方法的性能数字也无法复现。


11. 推荐工具链 ​

层级工具
C++ 构建CMake、GCC/Clang/MSVC
CPU 并行std::thread、OpenMP、oneTBB
CPU 分析perf、VTune、火焰图
GPU 编程CUDA、cuBLAS、Thrust
GPU 分析Nsight Systems、Nsight Compute
分布式Open MPI / MPICH、NCCL
数值验证Python、NumPy、单元测试
可视化Python、Matplotlib、VTK

12. 完成专题后应该能回答 ​

  1. 为什么增加核心后加速比会逐渐下降?
  2. 为什么连续访问比随机访问快?
  3. CPU SIMD 和 GPU SIMT 有什么区别?
  4. 为什么高 Occupancy 不保证高性能?
  5. 什么时候程序受内存带宽限制?
  6. CPU-GPU 传输为什么可能抵消加速收益?
  7. AllReduce 为什么会限制多机训练扩展?
  8. 怎样用证据说明一个优化确实有效?

如果能结合自己的实验回答这些问题,就已经建立了较完整的并行计算知识框架。

返回专题首页:[[00-parallel-computing-overview]]


完整迁入:原并行应用案例全文 ​

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

📅 创建时间:2026-07-20 🏷️ 标签:#AI训练 #CAE #图像处理 #科学计算 📚 前置知识:[[10-performance-engineering]] 📚 相关知识:[[/04-ai/01-llm-engineering/13-training-infrastructure]] [[/05-industrial-software/01-foundations-and-architecture/03-cae-basics]]


1. AI 训练 ​

核心计算 ​

Transformer 训练的大部分计算来自矩阵乘法和 Attention,适合 GPU/Tensor Core。

系统并行 ​
text
CPU 数据处理
  ↓
GPU 前向与反向传播
  ↓
多 GPU 梯度同步
  ↓
优化器更新与 Checkpoint
1
2
3
4
5
6
7

主要瓶颈可能是:

  • GPU 算力
  • HBM 带宽
  • 激活和优化器显存
  • GPU 间集合通信
  • 数据加载和存储

优化手段包括混合精度、算子融合、Activation Checkpoint、数据/张量/流水线并行和通信重叠。


2. AI 推理 ​

推理同时关心吞吐与响应延迟:

  • Batching 提高吞吐,但会增加排队延迟
  • KV Cache 减少重复计算,但消耗显存
  • 量化减少容量和带宽,可能影响精度
  • Continuous Batching 提高动态请求利用率
  • Speculative Decoding 用额外计算换取更少串行解码步骤

自回归解码存在 Token 间依赖,不能简单把整个序列一次并行生成。


3. CAE 与有限元 ​

典型流程:

text
网格读取 → 单元计算 → 矩阵组装 → 线性求解 → 后处理
1
单元计算 ​

不同单元可以并行,但向全局矩阵 Scatter 时可能产生写冲突。

稀疏线性求解 ​

稀疏矩阵向量乘法通常算术强度低,容易受内存带宽限制。预条件器和收敛次数会显著影响总时间。

多节点划分 ​

网格分区需要兼顾单元数量和边界规模,边界越大,Halo Exchange 越多。


4. CFD 与规则网格 ​

有限差分和有限体积常使用 Stencil:

  • CPU:多线程 + SIMD + Cache 分块
  • GPU:Block + Shared Memory Tile
  • 集群:空间分区 + Halo Exchange

时间步之间存在依赖,但同一时间步的大量网格点可以并行。


5. 图像与视频处理 ​

像素级滤波天然适合 GPU,但完整流水线还包含:

text
磁盘/相机输入 → 解码 → 色彩转换 → 滤波 → 编码 → 输出
1

只加速滤波 Kernel 可能无法改善被解码或 I/O 限制的系统。使用硬件编解码、流水线和设备内数据复用通常更重要。


6. 图计算 ​

图算法具有不规则访问和动态工作量:

  • 顶点度数差异巨大
  • 邻接表访问不连续
  • Frontier 大小动态变化
  • 原子更新较多

CPU 擅长复杂控制,GPU 仍可通过 Frontier 压缩、负载分配和批处理获得高吞吐,但优化难度高于规则矩阵计算。


7. 数据库与数据分析 ​

并行技术应用于:

  • 分区扫描
  • Hash Join
  • 聚合与排序
  • SIMD 向量化执行
  • 多核查询调度
  • GPU 数据库算子

列式存储提高连续访问和压缩效率,也更适合向量化。查询执行计划决定并行是否真正减少总工作量。


8. 领域选型表 ​

场景主要并行方式常见瓶颈
LLM 训练GPU + 多卡集合通信算力、显存、网络
LLM 推理Batching + GPU KernelHBM、KV Cache、延迟
有限元CPU/GPU + MPI稀疏访存、通信
CFDStencil + 空间分区带宽、Halo Exchange
图像视频GPU + Pipeline传输、编解码
图计算动态任务并行随机访存、负载不均
数据分析SIMD + 多核分区内存带宽、数据倾斜

核心总结 ​

  • 领域算法决定并行模式,不能从硬件型号反推算法。
  • 规则密集计算更容易利用 GPU,不规则任务更依赖调度和数据结构。
  • 端到端系统常包含 CPU、GPU、网络和存储的组合瓶颈。
  • AI 与 CAE 都会同时遇到计算、容量和通信问题。

下一篇:[[12-learning-projects]]


完整迁入:原深度学习训练优化案例全文 ​

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

📅 创建时间:2026-06-02 🏷️ 标签:#DL #训练优化 #混合精度 #GradientAccumulation #DeepSpeed #ZeRO 📚 前置知识:[[08-mpi-cluster-hpc]](分布式训练) [[06-cuda-optimization]](CUDA 优化) 📚 相关知识:[[/04-ai/01-llm-engineering/09-transformer-training-computation]](训练计算详解) [[/04-ai/01-llm-engineering/13-training-infrastructure]](训练基础设施)


先抓住直觉 ​

训练优化是在速度、显存和数值稳定性之间做交换:混合精度减少字节数,梯度累积用更多步骤模拟大批量,Checkpointing 用重复计算换显存,ZeRO 把冗余状态分散到多张卡。

  • 必须理解:每种技术省下的是什么、额外付出的是什么。
  • 用到再查:PyTorch/DeepSpeed 配置字段和具体 API。
  • 避免误区:这些收益不能简单相乘,最终效果取决于实际瓶颈和通信开销。

场景:你的训练为什么跑得比预期慢? ​

┌─────────────────────────────────────────────────────────────┐
│                                                             │
│  你的训练配置:                                            │
│  - 模型:175B 参数                                         │
│  - 硬件:8 × A100 80GB                                   │
│  - batch size:8(每卡)                                  │
│  - 精度:FP32                                             │
│  - 优化器:Adam                                           │
│  - 预计时间:90 天                                        │
│                                                             │
│  同行竞争者的配置:                                        │
│  - 模型:175B 参数                                         │
│  - 硬件:8 × A100 80GB                                   │
│  - batch size:16(每卡)+ 梯度累积 8                     │
│  - 精度:FP16(混合精度)                                 │
│  - 优化器:Adam + DeepSpeed ZeRO-3                       │
│  - 预计时间:3 天                                         │
│                                                             │
│  差距在哪里?本章告诉你。                                  │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

第1节:混合精度训练——显存减半,速度翻倍 ​

为什么需要混合精度? ​
FP32(32位浮点):
- 存储:4 字节/参数
- 计算:单精度
- 显存占用:高
- 速度:慢

FP16(16位浮点):
- 存储:2 字节/参数
- 计算:半精度
- 显存占用:FP32 的一半
- 速度:Tensor Core 快 8-32 倍

但 FP16 有精度问题:
- 表示范围小:FP16 max = 65504,FP32 max = 3.4e38
- 大模型训练中,梯度可能超出 FP16 表示范围

解决方案:混合精度训练
- Forward/Backward:FP16(快、省显存)
- Optimizer states:FP32(高精度)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
混合精度训练流程 ​
┌─────────────────────────────────────────────────────────────┐
│                    混合精度训练流程                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Forward(FP16):                                         │
│  1. 权重备份 FP32 → FP16                                 │
│  2. FP16 前向传播 → loss                                 │
│  3. Loss 缩放(loss scaling,防止下溢)                    │
│                                                             │
│  Backward(FP16):                                       │
│  4. FP16 反向传播 → 梯度                                 │
│  5. Unscale 梯度                                          │
│                                                             │
│  Optimizer(FP32):                                      │
│  6. 梯度 FP16 → FP32(精度恢复)                         │
│  7. FP32 优化器更新 → FP32 权重                          │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
PyTorch 混合精度实现 ​
python
from torch.cuda.amp import autocast, GradScaler

### 训练循环
scaler = GradScaler()  # Loss 缩放器

for batch in dataloader:
    optimizer.zero_grad()

    # Forward(自动用 FP16)
    with autocast():
        output = model(batch.input)
        loss = criterion(output, batch.target)

    # Backward(自动处理缩放)
    scaler.scale(loss).backward()

    # Optimizer step
    scaler.step(optimizer)
    scaler.update()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
显存节省计算 ​
┌─────────────────────────────────────────────────────────────┐
│                    混合精度显存节省                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  FP32 训练(单卡,175B 模型):                            │
│  参数:700 GB                                             │
│  梯度:700 GB                                             │
│  优化器状态(Adam):1400 GB(2×FP32)                   │
│  激活值:~200 GB                                          │
│  总计:~3000 GB ≈ 需要 38 张 A100(80GB)               │
│                                                             │
│  FP16 混合精度训练(单卡):                               │
│  参数(FP16):350 GB                                      │
│  梯度(FP16):350 GB                                     │
│  优化器状态(FP32):1400 GB                              │
│  激活值(FP16):~100 GB                                  │
│  总计:~2200 GB ≈ 需要 28 张 A100(80GB)               │
│                                                             │
│  节省:显存减少约 25%                                     │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

第2节:梯度累积——模拟更大 batch size ​

为什么需要梯度累积? ​
问题:单卡显存有限,无法使用大 batch size

例如:
- 单卡最大 batch size = 4(显存刚好够)
- 有效 batch size = 4(太小,训练不稳定)

解决方案:梯度累积
- 累积多个小 batch 的梯度
- 累积够一个"虚拟"大 batch 后,再更新参数
1
2
3
4
5
6
7
8
9
梯度累积原理 ​
python
### 梯度累积示例
effective_batch_size = 32  # 想要的有效 batch size
micro_batch_size = 4       # 实际能装的 batch size
accumulation_steps = effective_batch_size // micro_batch_size  # = 8

optimizer.zero_grad()

for i in range(accumulation_steps):
    batch = dataloader[i]  # micro batch

    # Forward
    with autocast():
        output = model(batch)
        loss = criterion(output, batch.target)

    # Backward(只累积梯度,不更新参数)
    scaler.scale(loss).backward()

    # 每 accumulation_steps 步才更新一次
    if (i + 1) % accumulation_steps == 0:
        scaler.step(optimizer)
        scaler.update()
        optimizer.zero_grad()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
梯度累积 + 混合精度 ​
python
### 完整的训练步骤
scaler = GradScaler()

for epoch in range(num_epochs):
    for step, batch in enumerate(dataloader):
        with autocast():
            output = model(batch.input)
            loss = criterion(output, batch.target) / accumulation_steps

        scaler.scale(loss).backward()

        if (step + 1) % accumulation_steps == 0:
            scaler.unscale_(optimizer)

            # 梯度裁剪(避免梯度爆炸)
            torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)

            scaler.step(optimizer)
            scaler.update()
            optimizer.zero_grad()

            # 学习率调度
            scheduler.step()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

第3节:Activation Checkpointing——用时间换显存 ​

原理 ​
问题:训练 Transformer 时,中间激活值占用大量显存

以 175B 模型为例:
- 序列长度:2048
- 隐藏维度:12288
- 层数:96
- 激活值显存 ≈ 96 × 2048 × 12288 × 2B × 2(反向)≈ 100 GB

解决:Activation Checkpointing(梯度检查点)
- 不保存所有激活值
- 只保存每 N 层的输出
- 反向传播时重新计算被丢弃的激活值
1
2
3
4
5
6
7
8
9
10
11
12
python
### PyTorch Activation Checkpointing
from torch.utils.checkpoint import checkpoint

class TransformerLayer(torch.nn.Module):
    def forward(self, x):
        # Forward 时,不保存中间激活值
        x = checkpoint(self.attention, x)
        x = checkpoint(self.feed_forward, x)
        return x

### 或者对整个模型应用
model = torch.utils.checkpoint.checkpoint_sequential(
    layers,  # 模型的各层
    checkpoint_segments=8,  # 分成 8 段
    input
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
时间 vs 显存 trade-off ​
┌─────────────────────────────────────────────────────────────┐
│                    Checkpointing 效果                        │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  175B 模型训练:                                           │
│                                                             │
│  无 Checkpoint:                                           │
│  激活值显存:~100 GB                                      │
│  显存总计:~450 GB/卡                                     │
│  需要:6 张 A100(80GB)                                   │
│                                                             │
│  每 12 层一个 checkpoint:                                 │
│  激活值显存:~100/12 ≈ 8 GB                              │
│  显存总计:~360 GB/卡                                     │
│  需要:5 张 A100(80GB)                                   │
│  时间增加:约 30%(重新计算激活值)                        │
│                                                             │
│  每 24 层一个 checkpoint:                                 │
│  激活值显存:~100/24 ≈ 4 GB                              │
│  显存总计:~356 GB/卡                                     │
│  需要:5 张 A100(80GB)                                   │
│  时间增加:约 40%                                         │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

第4节:DeepSpeed ZeRO——超越数据并行 ​

ZeRO 三级分片 ​
┌─────────────────────────────────────────────────────────────┐
│                    ZeRO 分片策略                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ZeRO-1(优化器状态分片):                               │
│  每个 GPU 只保存 1/N 的优化器状态                         │
│  显存节省:4 倍                                           │
│  通信量:不增加                                           │
│                                                             │
│  ZeRO-2(梯度分片):                                     │
│  每个 GPU 只保存 1/N 的梯度                               │
│  显存节省:2 倍(叠加后 8 倍)                           │
│  通信量:略微增加                                         │
│                                                             │
│  ZeRO-3(参数分片):                                     │
│  每个 GPU 只保存 1/N 的参数                               │
│  显存节省:N 倍(理论上无限扩展)                        │
│  通信量:显著增加                                         │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
ZeRO-3 配置示例 ​
python
### deepspeed_config.json
{
    "zero_optimization": {
        "stage": 3,  # ZeRO-3
        "stage3_param_persistence_threshold": 1e4,
        "stage3_gather_16bit_weights_on_model_save": True,
        "contiguous_gradients": True
    },
    "fp16": {
        "enabled": True,
        "loss_scale": 0,
        "loss_scale_window": 1000,
        "initial_scale_power": 16
    },
    "gradient_clipping": 1.0
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
python
### train.py
import deepspeed

### 模型
model = TransformerModel()

### DeepSpeed 初始化
model_engine, optimizer, _, _ = deepspeed.initialize(
    model=model,
    config="deepspeed_config.json",
    training_data=train_dataset
)

### 训练循环
for batch in dataloader:
    loss = model_engine(batch.input, batch.target)
    model_engine.backward(loss)
    model_engine.step()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

第5节:完整训练配置示例 ​

┌─────────────────────────────────────────────────────────────┐
│                    175B 模型训练配置                         │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  硬件:64 × A100 80GB(8 节点 × 8 卡)                   │
│                                                             │
│  优化策略叠加:                                           │
│  1. 混合精度(FP16 Forward/Backward + FP32 Optimizer)      │
│  2. 梯度累积(effective batch = 4096)                    │
│  3. Activation Checkpointing(每 24 层一个 checkpoint)     │
│  4. DeepSpeed ZeRO-3(参数/梯度/优化器状态分片)            │
│  5. 流水线并行(8 个 stage)                              │
│                                                             │
│  显存使用(每卡):                                       │
│  参数(FP16):350/64 ≈ 5.5 GB                          │
│  梯度(FP16):350/64 ≈ 5.5 GB                          │
│  优化器状态(FP32):1400/64 ≈ 22 GB                     │
│  激活值(FP16):~30 GB(checkpoint 后)                  │
│  总计:~63 GB/卡 < 80 GB ✅                              │
│                                                             │
│  训练时间:                                               │
│  理论 TFLOPS 利用率:约 50%                              │
│  预计训练时间:~3 天                                      │
│                                                             │
└─────────────────────────────────────────────────────────────┘
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

升华:优化策略叠加效果 ​

┌─────────────────────────────────────────────────────────────┐
│                    30 天 → 3 天优化路径                      │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  基准:单卡 FP32,batch=1                                 │
│  时间:30 天                                              │
│                                                             │
│  第1步:FP16 混合精度 → 时间 ÷ 2 = 15 天              │
│  第2步:梯度累积(batch×8)→ 时间 ÷ 1.5 = 10 天     │
│  第3步:Activation Checkpointing → 时间 × 1.3 = 13 天  │
│  第4步:8 卡数据并行(ZeRO-3)→ 时间 ÷ 6 = 2.2 天   │
│  第5步:流水线并行(8 stage)→ 时间 ÷ 1.2 = 1.8 天  │
│                                                             │
│  实际效果:约 3 天                                       │
│                                                             │
│  关键:多种优化策略叠加,而不是单一优化                   │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

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

AI 可查:
✅ PyTorch AMP 的具体 API(GradScaler 参数)
✅ DeepSpeed ZeRO 的具体配置参数
✅ Activation Checkpointing 的具体函数

必须理解:
🔴 混合精度:Forward/Backward 用 FP16,Optimizer 用 FP32
🔴 梯度累积:用小 batch 模拟大 batch,不增加显存
🔴 Activation Checkpointing:用计算时间换显存
🔴 ZeRO 三级分片:优化器状态 → 梯度 → 参数
🔴 多种优化策略叠加 = 显存大幅减少 + 时间可接受
1
2
3
4
5
6
7
8
9
10
11

学习状态:🟡 开始学习 <!-- migrated-deep-dive:end -->

最终应能讲出的面试故事 ​

不要只说“用了 CUDA 提速”。完整叙述应是:先建立标量正确基线;通过 profiler 确认热点和带宽特征;选择数据/线程/进程划分;处理竞争、边界与传输;比较端到端数据;解释扩展停止的原因;说明未选择其他方案的依据。

结课自测 ​

  1. 同一任务为什么要保留所有层次的基线?
  2. CUDA 版本只报告 Kernel 时间有什么问题?
  3. MPI 强扩展到更多节点变慢时应检查什么?

答案:用于正确性、收益归因和回退;忽略传输、同步和 Host 阶段会夸大应用收益;检查串行占比、消息粒度、通信量、负载均衡和进程/NUMA 绑定。

最后更新于:

Pager
上一篇14. 性能模型与工具
下一篇1. 历史完整正文:统一前文章逐篇保留

持续记录,持续成长

Copyright © Tidenflow