AI 系统的分层
计算机科学解决复杂度只有一招:分层抽象。每层只依赖下一层的接口,不关心其实现。AI 系统也不例外——从晶体管到"用户提交了一个训练作业",中间隔着七层。
这一章给出全书使用的分层。它的用处不是背下来,而是:当性能不如预期时,你能说出问题在哪一层,以及为什么在别的层做优化没有用。
| # | 层 | 向上提供的承诺 | 向下隐藏的细节 | 时间尺度 | 对应卷 |
|---|---|---|---|---|---|
| 7 | 调度与平台 | "提交作业,它会跑完" | 排队、故障、资源竞争、多租户 | 分钟–天 | 八、十一 |
| 6 | 训练 / 推理框架 | "定义模型,它会算" | 自动微分、算子调度、显存管理 | 毫秒–秒 | 四、六 |
| 5 | 编译器与内核 | "这个算子会被高效执行" | 分块、融合、寄存器分配、指令选择 | 微秒 | 五 |
| 4 | 分布式运行时 | "多卡像一张卡" | 分片、通信、同步、重叠 | 微秒–毫秒 | 四 |
| 3 | 集群网络 | "任意两卡可通信" | 拓扑、路由、拥塞、重传 | 微秒 | 三 |
| 2 | 节点内互连 | "同机八卡高带宽互通" | 链路差异、NUMA、点对点能力 | 亚微秒 | 二 |
| 1 | 加速器与内存 | "这么多算力、这么多显存" | 核心调度、缓存、内存时序、功耗 | 纳秒 | 二 |
从下往上,每层的时间尺度大致放大一个数量级。这不是巧合:每一层都在用"多花一点时间"换"少操一份心"。
第 1 层 · 加速器与内存。 提供两个数字:峰值算力和显存容量。它隐藏了核心怎么调度、缓存怎么工作、显存怎么排队。泄漏点在于这两个数字都是"理想值"——实际能拿到多少取决于你的访问模式和功耗状态。下一章专门讲这些数字。
第 2 层 · 节点内互连。 让同一台机器里的几张卡能高速交换数据,通常比走主板总线快一个数量级。它是"八卡为一个单位"这个惯例的物理来源——很多并行策略的边界就画在这里。
第 3 层 · 集群网络。 让任意两张卡能通信。它隐藏了拓扑和路由,但隐藏得不彻底:两张卡的物理位置会让实际带宽差出好几倍,而框架看不到这一点。
第 4 层 · 分布式运行时。 最关键也最漏的一层。它承诺"多卡像一张卡",实际上你必须知道数据怎么切、通信量多大、能不能和计算重叠,否则加卡不加速。第六章和第十章讲这一层。
第 5 层 · 编译器与内核。 把"矩阵乘"这样的算子变成具体的机器指令,负责分块、融合、调度。它承诺"高效执行",但对没见过的算子形状、动态变化的张量尺寸,效率可能断崖式下降。
第 6 层 · 框架。 让你用几十行代码描述一个模型。它隐藏了求导、内存分配、算子调度。泄漏点是:同样的数学,不同的写法性能可以差几倍。
第 7 层 · 调度与平台。 承诺"提交就会跑完"。隐藏了排队、抢占、故障。泄漏点是:分到哪些机器会显著影响速度,而用户通常无法指定。
两条纵向关注点
Section titled “两条纵向关注点”有两件事不属于任何一层,却贯穿所有层:
- 数据(第七卷):从远端存储到加速器显存的一条独立管道。任何一段堵住,上面全部空转。它与七层正交,因为每一层都可能成为数据路径上的瓶颈。
- 可观测性(第九卷):每层都要能被测量,否则无法定位瓶颈。一个测不出来的系统,等于没有系统。
层与层之间的落差
Section titled “层与层之间的落差”分层之所以重要,是因为相邻层之间的性能差距是数量级的。下一章会给具体数字,这里先给结论性的直觉:
片上存储 ──10×──→ 显存 ──10×──→ 节点内互连 ──10×──→ 跨节点网络 ──100×──→ 本地盘 ──1000×──→ 远端存储每跨一级,带宽降一档、延迟升一档。这些落差解释了几乎所有系统设计决策:
| 设计 | 为什么 |
|---|---|
| 算子融合 | 少往显存跑一趟 |
| 张量并行只在节点内 | 跨节点带宽低一个数量级,通信会吃掉收益 |
| 训练数据缓存到本地盘 | 远端存储的延迟高两三个数量级 |
| 检查点先写本地再异步上传 | 同上,且不阻塞训练 |
| 增大批量 | 摊薄每次数据搬运的固定开销 |
记住落差,比记住绝对数字更有用——绝对数字每代硬件都在变,落差的相对关系几十年没变过。
瓶颈只有一个
Section titled “瓶颈只有一个”把一个训练步骤的墙钟时间拆开:
理想情况下后四项都能与计算重叠,。现实中总有一项没被藏住,它就是瓶颈。
在非瓶颈层做优化,端到端收益为零。 这听起来显然,但违反得极其频繁:花两周把某个算子优化了 30%,端到端毫无变化——因为作业本来卡在通信上。
用 Amdahl 定律量化:若某部分占总时间比例 ,把它加速 倍,端到端加速比为
代入几个数看看:
| 该部分占比 | 加速 2× | 加速 10× | 加速 ∞ |
|---|---|---|---|
| 10% | 1.05× | 1.10× | 1.11× |
| 30% | 1.18× | 1.37× | 1.43× |
| 50% | 1.33× | 1.82× | 2.00× |
| 80% | 1.67× | 3.57× | 5.00× |
占比 30% 的部分即使优化到无穷快,端到端也只快 43%。所以第一步永远是测量——不测就动手,大概率在优化那 10%。
分层能成立的前提是"每层的承诺可靠"。但 AI 系统的抽象泄漏得比一般软件严重得多,原因很简单:性能本身就是需求。一个 Web 框架慢 30% 通常无所谓,一个训练框架慢 30% 意味着多烧几百万。
| 层的承诺 | 泄漏形式 | 后果 |
|---|---|---|
| "定义模型即可" | 算子没被融合、写法不同 | 显存爆炸;速度差数倍 |
| "多卡像一张卡" | 不懂通信量、分片策略选错 | 扩展性归零;显存不足 |
| "算子会被高效执行" | 张量形状动态变化、图被打断 | 反复重编译;优化失效 |
| "任意两卡可通信" | 物理位置不佳、单条链路故障 | 慢 30%;全局被拖累 |
| "提交作业就会跑完" | 拓扑没对齐、被抢占 | 通信绕远路;从头再来 |
这就是 AI Infra 这个岗位存在的直接原因:上层的人需要有人替他们管理泄漏的抽象。 也是本书不能只讲某一层的原因——真实问题总是跨层的。
一次真实的自上而下排查
Section titled “一次真实的自上而下排查”用户报告"训练比上周慢了 20%"。按层排查,每层问一个可测量的问题:
| 层 | 问什么 | 怎么测 | 常见原因 |
|---|---|---|---|
| — 数据 | 加速器在等数据吗? | 时间线上有无规律性空隙 | 数据源变慢、预处理并发不足 |
| 7 调度 | 换机器了吗? | 对比作业的节点列表 | 拓扑变差、与人混部 |
| 6 框架 | 配置变了吗? | diff 配置与代码 | 批量、重计算、并行度变化 |
| 5 编译器 | 版本变了吗? | 检查镜像与库版本 | 内核回退到慢路径 |
| 4 运行时 | 通信占多少时间? | 通信计时、时间线 | 重叠失效、并行度不当 |
| 3 网络 | 带宽还是基线吗? | 跑标准通信基准测试 | 链路误码、拥塞、路由不均 |
| 2 节点内 | 卡间链路正常吗? | 查链路状态与速率 | 降速、卡间不对称 |
| 1 加速器 | 时钟被压了吗? | 采功耗、温度、时钟频率 | 功耗封顶、散热问题 |
真实排查中最常见的答案是第 3 层(一条链路)和第 1 层(一张降频的卡),而不是代码。 顺序建议:先怀疑硬件与环境,再怀疑自己最近的改动,最后才怀疑框架。
理由是概率:硬件天天在坏,你的代码上周还是好的,框架被几十万人用过。
分层与本书的对应
Section titled “分层与本书的对应”读完这一章,你应该能把任何一个问题映射到某一层,然后直接跳到对应的卷:
性能 / 成本问题├─ 加速器在等数据 → 第七卷 存储与数据管道├─ 加速器在等通信 → 第三卷 网络 / 第四卷 并行策略├─ 加速器在算但算得慢 → 第五卷 编译器与内核 / 第二卷 硬件├─ 显存不够 → 第四卷 分片与重计算 / 本卷第五章 精度├─ 卡拿不到 / 排队太久 → 第八卷 调度与集群├─ 时快时慢 / 经常挂 → 第九卷 可观测性与可靠性├─ 能跑但太贵 → 第十卷 效率与成本└─ 不知道卡在哪 → 本卷第四、七章(先学会测量)最后一行最重要。如果你还不知道问题在哪一层,不要跳到任何一卷——先读完本卷第二部分,学会判断。
- 七层:加速器 → 节点内互连 → 集群网络 → 分布式运行时 → 编译器与内核 → 框架 → 调度平台;外加数据与可观测性两条纵向关注点。
- 相邻层之间是数量级的落差;记住落差比记住绝对数字有用。
- 任一时刻只有一个瓶颈,非瓶颈层的优化端到端收益趋近于零——Amdahl 定律给出上界。
- AI 系统的抽象泄漏严重,因为性能本身是需求;管理泄漏正是这个岗位的价值。
- 排障顺序:先硬件与环境,再自己的改动,最后框架——按概率排的。
- 为什么会有 AI Infra — 上一章
- 硬件的基本量 — 下一章,给出本章"落差"的具体数字
- 效率度量 — 怎么测量每一层