Lecture 16 · 总结与课程项目:全栈复盘、部署规划与怎样做一个推理系统项目 · Wrap-up & Course Project

Efficient ML Inference at Scale · Lecture 16。个人学习讲义与配套实验,按主题整理并持续修订。

课程源码 ZIP ↓

个人整理的学习笔记,非官方讲义;课程来源与整理说明见课程介绍。配套示例未逐一运行验证。

本篇目录

对应 Maps to:Stanford CS349E / EE339 Efficient ML Inference at Scale(Autumn 2026),原课第 9 次课(10/20 · Project Pitches)和第 18–19 次课(12/1、12/3 · Project Presentations)。这三次课是学生的项目提案和展示,没有讲授内容;原课评分里项目占 40%。 依据说明:本讲是我写的总结:§1 把前 15 讲串起来,§2 用前面各讲的模块搭了一个端到端的部署规划器,§3 讲做项目的方法(借用了第 14 讲、本仓库 CS349D 第 17 讲的评测方法),§4 列开放问题。原课的第 13 次课(客座讲座)没有公开内容,不覆盖。不代表课堂上实际讲了什么。 配套代码 Code:code/lecture16/(numpy;一个模块 + 4 个脚本,最长的约 25 秒) 先修:全部前 15 讲。规划器直接用到 第 1 讲(账本)、第 3 讲(精度)、第 4 讲(张量并行)、第 5 讲(服务模拟器)、第 6 讲(投机解码)、第 12 讲(内核个数)。 预计阅读时间:2–2.5 小时(含运行代码)


0. 本讲导航 · How to read this lecture

0.1 学习目标 Learning goals

学完这一讲,你应该能够:

  1. 把一个 token 的生成拆成”读权重 / 读 KV / 计算 / 通信 / 内核启动 / 排队”几项,说出每一项归哪一讲管、对策是什么。
  2. 解释”优化阶梯”上每一级的收益从哪里来,区分哪些技术改善单个用户的延迟、哪些改善吞吐和成本。
  3. 给定模型、负载、SLO 和价格,用”解析模型筛选 + 模拟验证”选出部署方案(GPU、精度、张量并行度、共置还是解耦、几块 GPU),并说出解析模型会在哪里出错。
  4. 设计一个推理系统的课程项目:选题、基线、测量方法、模型与实测的关系、消融、写报告。
  5. 说出这个领域在 2026 年的几个开放问题。

0.2 本讲地图 Map

§1  全栈复盘:一个 token 的账与优化阶梯                             ← 代码 01
§2  端到端的部署规划器                                              ← 代码 02、03、04
§3  怎样做一个推理系统项目
§4  开放问题
§5–9  小结、常见误区、练习(含答案)、延伸阅读、结语

0.3 本讲的代码

planner.py 有三部分:

  • A. 一个 token 的全栈账:tp_step 把张量并行下一次前向的时间拆成读权重、读 KV、计算、all-reduce、内核启动(用第 1、4 讲的账本,P5 的 α–β 公式,第 12 讲 graph_lab 数出来的每层内核数);ladder_point 算优化阶梯上的一级(含第 6 讲的投机解码)。
  • B. 部署规划:Scenario(模型、提示 / 输出长度、TTFT / TPOT 的 P90 SLO、流量);decode_capacity、prefill_capacity、colocated_capacity(解析模型);plan 枚举 {H100, H200, B200} × {BF16, FP8} × 张量并行 {1, 2, 4, 8} × {共置, 解耦},按每百万输出 token 的成本排序。
  • C. 验证:simulate 用第 5 讲的离散事件模拟器跑一个方案;repair 不达标就加实例;verified_best 把两步串起来。

0.4 记号 Notation

符号含义
TTFT、TPOT第一个 token 的时间、之后每个 token 的间隔;SLO 都按 P90
共置 colocatedprefill 和 decode 在同一组 GPU 上,用分块 prefill 混在一起跑(第 5 讲)
解耦 disaggregatedprefill 实例和 decode 实例分开,KV 通过网络搬(第 5 讲)
TPkk一个实例用 kk 块 GPU 做张量并行(第 4 讲)
“按整数台”的成本实际要租的 GPU 数 × 价格 / 产出的 token
“按效率”的成本如果 GPU 能按小数台租:每块 GPU 的吞吐决定的成本

1. 全栈复盘:一个 token 的账 · The full-stack ledger

代码:01_full_stack_ledger.py

1.1 五条主线,最后一次

整门课一直在重复五句话(SYLLABUS §0.3):

  1. 能量和带宽,而不是算力,是推理的主要约束。45 nm 下读 8 字节 DRAM 是一次 32 位浮点乘法的 351–703 倍(第 1 讲);性能 = 能效 × 功率。
  2. roofline 是贯穿全课的尺子:H100 BF16 的屋脊点约 295 FLOP/字节;decode 注意力的强度就是 GQA 的组大小(第 1、2 讲),永远到不了屋脊点。
  3. LLM 推理的两种”性格”:prefill 吃算力,decode 吃带宽和容量;KV cache 是批处理摊不薄的部分。
  4. 同一个瓶颈可以在任何一层去攻:模型(GQA、MoE)、精度(FP8、NVFP4)、服务系统(批处理、分页、解耦)、硬件(HBM、SRAM、Tensor Core)、内核(分块、FlashAttention、megakernel)、编译器(融合、调度、自动调优)——代价各不相同。
  5. 异构与解耦:一个请求的不同阶段适合不同的硬件和并行方式(第 5 讲;本讲 §2.4 会在一个极端的 SLO 下再看到它)。

1.2 一个 token 的账

Llama-3.1-70B 在 8 块 H100 上(张量并行 8、上下文 4096)decode 一步(01 演示 1):

配置读权重读 KV计算通信内核启动一步
批 1,eager6.48 ms0.06 ms0.03 ms0.98 ms8.64 ms16.16 ms
批 1,融合 + CUDA Graphs6.480.060.030.980.968.48
批 64,融合 + CUDA Graphs6.484.012.022.080.9613.53

每一项归哪一讲管:

项由什么决定对策(讲次)
读权重参数量 × 每个数的字节 / 带宽低精度(3)、批处理摊薄(1、6)、更大带宽的硬件(8)
读 KV批 × 上下文 × 每 token 的 KVGQA / MLA(2)、KV 量化(3)、分页与前缀缓存(7)
计算FLOPs / 峰值Tensor Core(8、10)、FlashAttention(11)、低精度的算力(3)
通信每层 2 次 all-reduce 的 α–β切分方式(4、P5)、计算和通信重叠
内核启动层数 × 每层内核数 × 每个的开销融合(12)、CUDA Graphs / megakernel(11)
排队与干扰到达过程、prefill / decode 混跑连续批处理(6)、分块 prefill / 解耦(5)、KV 调度(7、15)

三个容易忽略的事实:

  • 批 1 的 eager 里,内核启动比读权重还多:80 层 × 27 个内核 × 4 µs = 8.64 ms(每层的内核数是第 12 讲的 graph_lab 数出来的:eager 27 个、融合 12 个)。这就是 megakernel(第 11 讲)、融合和 CUDA Graphs(第 12 讲)要解决的问题。
  • 张量并行的通信每步约 1–2 ms:批 1 时消息很小,几乎全是每次 all-reduce 的延迟 α × 160 次;批大了带宽项才上来(第 4 讲)。
  • prefill 是另一副面孔(演示 3):一次 2,048 个 token 的 prefill,计算 61.13 ms 远大于读权重 6.48 ms(计算受限),通信也涨到 25.36 ms。

1.3 优化阶梯

从”最朴素的部署”出发,逐个加上各讲的技术(演示 2;每块 H100 按 2 美元 / 小时、700 W 算):

部署每个用户 token/s每块 GPU token/s美元 / 百万 token焦耳 / token
0. 朴素:批 1、BF16、eager62871.8490.5
1. + CUDA Graphs(11、12)1031343.0454.2
2. + 融合(12)1181537.7047.5
3. + 连续批处理到批 64(6、7)745910.941.18
4. + FP8 权重和 KV(3)1219660.580.72
5. + 投机解码 γ = 4、α = 0.8(6)1581,2610.440.56
(对照)批 1 + FP8 + 投机解码3424312.9816.4
  • 从第 0 级到第 5 级,每块 GPU 的吞吐提高 163 倍。最大的一步是批处理:decode 受限于读权重,64 个用户分摊同一次读取——第 1 讲 roofline 的直接推论。
  • 前两级(CUDA Graphs、融合)主要改善单个用户的延迟;批处理提高吞吐,但每个用户的速度反而下降(每步多读 KV)——延迟和吞吐的取舍。
  • FP8 同时减少读权重和读 KV 的字节,把单用户速度补回来;投机解码在批 64 时仍有收益,但远不如批 1 时(对照行):批越大,验证 γ+1 个 token 越接近计算受限(第 6 讲 §5)。
  • 这条阶梯上的每一级都只用了前面某一讲的模型。真实系统里它们会相互影响(显存共享、投机解码和批处理争算力),所以最后一步总要端到端地测,或至少端到端地模拟(§2.3)。

2. 端到端的部署规划器 · An end-to-end deployment planner

代码:02_deployment_planner.py、03_verify_with_simulation.py、04_what_if.py

2.1 问题

一个推理服务上线前要回答:**用哪种 GPU、BF16 还是 FP8、张量并行几路、prefill 和 decode 放在一起还是分开、要几块 GPU?**目标是在满足 SLO 的前提下让每百万输出 token 的成本最低。四个场景:

场景模型提示 / 输出(平均,对数正态)SLO(P90)流量
聊天Llama-3.1-70B1000 / 250TTFT ≤ 1 s、TPOT ≤ 50 ms20 请求/s
写代码的智能体Llama-3.1-70B8000 / 400TTFT ≤ 3 s、TPOT ≤ 50 ms4 请求/s
离线批处理Llama-3.1-70B2000 / 300TTFT ≤ 60 s、TPOT ≤ 0.5 s20 请求/s
低延迟(小模型)Llama-3.1-8B500 / 100TTFT ≤ 0.2 s、TPOT ≤ 15 ms40 请求/s

搜索空间:{H100, H200, B200} × {BF16, FP8} × 张量并行 {1, 2, 4, 8} × {共置(分块 prefill), 解耦}。GPU 价格是假设值(H100 2.0、H200 2.6、B200 4.0 美元 / 小时),只用来比较。

2.2 解析模型

每步时间都来自第 5 讲的 StepModel(roofline + 60% 算力、80% 带宽的折扣 + 每步 1 ms 的固定开销 + 张量并行的 all-reduce)。

  • decode 实例:KV 按”提示 + 全部输出”预留,批大小 ≤ 显存容量 / (P + O);在这之下找最大的批 bb,使一步的时间 ≤ 0.85 × TPOT(留 15% 给长短不一和 P90 的尾巴)。吞吐 = bb / (一步 × 平均输出长度)。
  • prefill 实例:M/D/1 排队(第 5 讲 §4):平均等待 W=ρSb/(2(1−ρ))W = \rho S_b / (2(1-\rho));用 P90≈S+2.3 W\text{P90} \approx S + 2.3\,W 近似尾部(2.3 ≈ ln 10),解出满足 TTFT 的最大利用率。
  • 共置实例:稳态下每一步 = bb 个 decode + 一块 cc 个 prefill token;要求一步 ≤ 0.85 × TPOT、⌈P/c⌉\lceil P / c \rceil 步 ≤ TTFT / 2;吞吐 = min⁡(b/O,c/P)\min(b/O, c/P) / 一步。在 c∈{256,512,1024,2048}c \in \{256, 512, 1024, 2048\} 和 bb 上搜索。
  • 解耦:和 DistServe 的算法 1 一样,prefill、decode 各自挑”每块 GPU 吞吐最高”的张量并行度,再各自复制到满足流量。

02 演示 1 的结果(解析模型):聊天最便宜的是 2 块 B200(FP8、共置、各自单卡),0.444 美元 / 百万输出 token,4 块 H100(FP8、共置、TP2 × 2)一样便宜;写代码的智能体约贵 3 倍(1.389,提示长 8 倍:每个输出 token 分摊的 prefill 多、KV 大、批小);离线批处理 0.556;低延迟的 8B 一块 H100 就够(0.139)。

按”每块 GPU 的效率”比较(演示 3,美元 / 百万输出 token,共置 / 解耦):

场景H100 BF16H100 FP8H200 BF16H200 FP8B200 BF16B200 FP8
聊天0.85 / 1.100.39 / 0.520.93 / 1.140.44 / 0.540.55 / 0.790.27 / 0.35
写代码的智能体3.61 / 6.611.67 / 4.834.70 / 8.602.17 / 4.482.73 / 5.181.21 / 2.70
离线批处理1.23 / 1.720.58 / 0.821.42 / 1.790.67 / 0.850.84 / 1.400.41 / 0.65
低延迟0.10 / 0.240.05 / 0.080.13 / 0.210.06 / 0.100.08 / 0.130.04 / 0.07
  • FP8 每一格都更便宜(字节减半,批更大,第 3 讲)——前提是质量允许。
  • B200 的效率最高:带宽 2.4 倍、显存 2.4 倍、算力 2.5 倍,价格只贵 1 倍。H200 在这四个场景里都比 H100 略贵:它贵 30%,多出来的带宽和显存只帮了 decode,prefill 的算力一样。
  • 共置几乎总是比解耦便宜:分块 prefill 已经控制住了干扰,解耦还要付 KV 传输和两边各自取整的代价。

演示 2 还说明了整数台的影响:按效率 B200 是 0.274 美元 / 百万,可 20 请求/s 时要 2 块 B200、容量是需要的 1.6 倍——多出来的是闲置。流量小的时候,“凑整”的浪费可能比方案之间效率的差别还大;办法是挑容量合适的配置、多个模型 / 租户共享 GPU(CS349D 第 12 讲)或弹性伸缩。

2.3 用模拟器检验:解析模型哪里会错

03 脚本用第 5 讲的离散事件模拟器检验这些方案(SLO 达成率按中间 60% 的请求统计,目标 90%)。

教训一:SLO 是 P90,就要用 P90 的长度去检查。写代码的智能体的提示长度是长尾的(平均 8,000,P90 16,195)。按平均长度筛选出的前三个方案在模拟器里都不达标(达成率 70%、76%、44%,TTFT 的 P90 是 4.3、4.6、10 秒);其中 H200 单卡的方案加多少实例都救不回来——单卡、每步 256 个 token 的分块 prefill,把一个一万多 token 的提示 prefill 完就要好几秒,这不是排队的问题,加机器不能让单个请求变快。改成按 P90 的提示长度检查 TTFT,这类不可行的方案就被去掉了。

**教训二:解析模型的排队近似对长短悬殊的请求偏乐观。**按 P90 筛选之后,写代码的智能体的前 6 个方案里仍有 3 个要加实例:最便宜的从 2 块 B200 变成 3 块(1.389 → 2.083 美元 / 百万,达成率 97%)。其他三个场景的方案直接通过。

**教训三:模拟要跑到稳态。**用二分查找量一个方案能承受的最大流量时,离线批处理(TTFT 限制 60 秒)的结果随模拟长度变化:

模拟的长度量到的最大流量(请求 / 秒,解析值 27.0)
固定 1,500 个请求≥ 107.9
至少 120 秒的到达45.6
至少 300 秒的到达33.3
至少 600 秒的到达30.0

过载的系统要过一段时间队列才会长到违反 SLO;只模拟十几秒的到达,会把一个不稳定的系统误判成”满足 SLO”(CS349D 第 17 讲 §5.2 讲过同一件事)。模拟够长之后(600 秒),解析容量和模拟的比值:聊天 0.91、离线批处理 1.11、写代码的智能体(单个实例)0.69——同一个解析模型,在不同的负载上偏差的方向都可能不同。

所以规划器的正确用法是**“解析模型筛选 + 模拟(或真机)验证”**:解析模型便宜,可以扫几百个组合;模拟器慢,但看得见排队和长尾。解析模型给出的成本只是一个估计,最后的数以验证后的为准。DistServe 的放置算法也是这样设计的。

2.4 假设一变,答案就变

04 脚本(每个结果都经过模拟检验和修复):

  • TPOT 收紧(聊天):从 100 ms 到 15 ms 成本都是 0.444 美元 / 百万(整数台留下的余量吸收了收紧的 SLO;15 ms 时换成张量并行 2)。收紧到 10 ms,最便宜的方案变成 H200、FP8、解耦(3 个单卡 prefill + 1 个 TP4 的 decode),1.011 美元 / 百万——这是本讲所有实验里解耦唯一一次胜出:decode 要用大的张量并行才够快,prefill 用小的更省,共置只能用同一种并行方式,两头顾不上(DistServe 的论点)。
  • 不许用 FP8:聊天和写代码的智能体都贵 2.00 倍。如果 FP8 的质量损失可以接受(第 3 讲用 KL、困惑度、下游任务来衡量),这部分钱就能省下来。规划器把质量当作约束,评估质量要另外做。
  • 硬件与价格:默认价格下 2 块 B200 和 4 块 H100 恰好一样贵(2 × 4.0 = 4 × 2.0);B200 涨到 6 美元后 H100 胜出;H200 降到 2.2 美元仍然不够。规划器的输出永远是”在这组价格、这个流量下”的答案。
  • 模型大小:同样的聊天 SLO,8B 0.111、70B 0.444、405B 3.556 美元 / 百万(405B 要 16 块 B200,TP4 × 4)。“用多大的模型”本身就是推理效率里最重要的决定之一。
  • 流量:5 请求/s 时按整数台的成本 0.578、按效率 0.440;500 请求/s 时 0.276 vs 0.274——流量大了,整数台的浪费消失,选择也更接近效率最高的配置。

2.5 这个规划器没有做的

为了诚实:它不考虑前缀缓存(智能体的长提示大部分可复用,第 7、15 讲)、投机解码(只在 01 的阶梯里)、流水线并行、MoE、多模型共享、弹性伸缩、故障;质量只是一个”允许 / 不允许 FP8”的开关;每步时间是 roofline 模型而不是实测;价格是假设的。把其中任何一项加进去,都是一个合格的课程项目(§3.7)。


3. 怎样做一个推理系统项目 · How to do a project

3.1 选题

好的推理系统项目通常从下面几种问题之一出发:

  • 一个瓶颈:用 roofline(第 1 讲)或 profiler 找到时间花在哪里,然后去攻它(§1.2 的那张表就是一个瓶颈清单)。
  • 一种负载:长上下文、智能体(多轮、工具暂停)、多模态、批处理——新负载往往让旧的假设失效(第 15 讲的 KV 保留就是例子)。
  • 一种硬件变化:FP4 / NVFP4、更大的 HBM、SRAM 芯片、新的互连——旧的取舍点会移动。
  • 一个评测问题:现有的基准测的对不对(第 14 讲 §3、§4)?

把题目缩小到能在一个学期里得出清楚结论的大小:一个问题、一个指标、一个有说服力的实验。

3.2 基线

  • 强、调好:和最好的已有方法比(vLLM / SGLang 的默认设置往往不是最好的设置);给基线调参的力气不能比给自己的方法少。
  • 可比:同样的硬件、同样的精度(第 14 讲 §4.5 的 TF32 教训)、同样的负载、同样的 SLO。
  • 问自己:**谁会对这个结果感到惊讶?**如果领域里的专家看了说”当然”,要么结果太弱,要么基线太弱。

3.3 测量

第 14 讲 §4 和 CS349D 第 17 讲 §5 的清单:

  1. 写明测量条件:硬件、软件版本、cache 冷热、同步方式、预热、重复次数。
  2. 交替测量对比的两方(A B A B),报告中位数和置信区间;不要只报一次的数字。
  3. 用一组测量挑候选,用另一组独立的测量报告它(挑赢家的偏差)。
  4. 问题要足够大(固定开销不要主导),实验要跑到稳态(§2.3 的教训三)。
  5. 服务系统要分清开环(按到达率发请求)和闭环(固定并发),报告 SLO 达成率和分布,而不只是平均值。
  6. 每个”变快了”的结果都要附上”还是对的”的证据(第 14 讲 §2–§3)。

3.4 模型与实测

这门课大量使用模型:roofline、解析的容量估计、离散事件模拟器。它们的价值在于便宜地探索和解释为什么;它们的风险在于把假设当成结论。好的做法:

  • 用少数几个真实的测量点校准模型(例如测一下实际的每步时间,替换 StepModel 的效率折扣);
  • 报告模型和实测的差距(本仓库每一讲的 README 都有一节”关于实验结果”,记录写作时和预想不一致的地方——这是一个值得模仿的习惯);
  • 用模型筛选、用更真实的方法确认(§2.3)。

3.5 消融与敏感性

一次只改一处(第 14 讲 §6.5 的消融);把关键假设(SLO、负载、价格、硬件)扫一遍,看结论在哪里翻转(§2.4)。种子之间的波动要和效果的大小一起报告——5 个种子的中位数差 0.02 说明不了什么。

3.6 写报告

  • 每一个主张对应一个实验;每一张图回答一个问题。
  • 写清楚局限:哪些是模型、哪些是实测;在什么范围内成立。
  • 写下没成功的尝试和意外:它们常常是最有价值的部分。

3.7 项目点子(从本仓库的代码出发)

点子起点
给规划器加上前缀缓存(智能体的长提示可复用),看最优方案怎样变本讲 planner.py + 第 7 讲 kv_store.py
给规划器加上投机解码和 MoEplanner.py + 第 6 讲 decode_lab.py、第 3 讲 quant_lab.py
用真机测量校准 StepModel,比较模型和实测的差距第 5 讲 serving_sim.py
实现 InferCept 的”最小浪费”策略或 Continuum 的 TTL 预测,接到智能体服务模拟里第 15 讲 agent_lab.py
程序级调度(Autellix)+ KV 保留的联合设计第 15 讲 + CS349D 第 13 讲
给 KernelBench 式的评估器再加几道防线,找一个新的作弊方式第 14 讲 kbench.py
用真正的 LLM 替换模拟的提议器,进化分块启发式第 14 讲 evolve.py
FP4 / NVFP4 的质量 vs 吞吐前沿第 3 讲、P3 numfmt.py
decode 的 megakernel 时间线:哪些开销能真正消掉第 11 讲 flash_lab.py
动态形状和 CUDA Graphs 的取舍(按桶补齐 vs 重新编译)第 12 讲 graph_lab.py
稀疏注意力 / 长上下文下的 KV 压缩,对 roofline 的影响第 2、3、11 讲
能量账:每个 token 多少焦耳,哪一层最费电第 1 讲 hw_specs.py + 本讲阶梯

4. 开放问题 · Open problems(2026)

  1. 内存墙:算力每代翻倍,HBM 的带宽和容量增长慢得多,价格和供应也受限。decode 和长上下文直接撞在这堵墙上;近存计算、SRAM 芯片、分层的 KV 存储(第 7、8 讲)是不同的绕法。
  2. 长上下文:KV 随上下文线性增长,注意力的计算随上下文增长;稀疏 / 混合注意力、KV 压缩、外存上的 KV 都在发展,但质量和效率的取舍还没有定论。
  3. 智能体负载:多轮、输入远多于输出、频繁暂停、跨请求的前缀共享(第 15 讲)——推理引擎的调度、缓存、KV 保留都要为它重新设计。
  4. 异构与解耦走到底:prefill / decode、注意力 / FFN、草稿 / 验证,放在不同的芯片上;代价是数据搬运和协调(第 5 讲)。什么样的拆分在什么规模下划算?
  5. 低精度的边界:FP8 已经普及,FP4 / NVFP4 的质量代价随模型和任务而变;训练和推理的精度怎样协同(第 3 讲、P3)?
  6. 内核与编译器追不上新硬件:每代硬件都要新内核;编译器(第 12、13 讲)和 AI 智能体(第 14 讲)都在尝试自动化,但评估它们本身就是一个难题。
  7. 评测的可信度:从内核基准(第 14 讲)到服务基准(CS349D 第 17 讲),每一次加固评估,报告的数字都下降一截。可复现、难作弊的评测方法本身就是研究课题。
  8. 能耗与成本的完整账:每个 token 的焦耳、每瓦的 token 数、数据中心的电力约束(第 1 讲)——效率最终要落到这几个数上。

5. 小结 · Summary

  1. 一个 token 的时间可以拆成读权重、读 KV、计算、通信、内核启动和排队;每一项都有对应的讲次和对策。批 1 的 eager decode 里,内核启动可以比读权重还多。
  2. 优化阶梯:批处理是最大的一步(每块 GPU 的吞吐 ×40),低精度和投机解码再往上推;有的技术改善延迟、有的改善吞吐,两者常常互相拉扯。
  3. 部署规划 = 在 {硬件, 精度, 并行度, 共置 / 解耦, 数量} 里找满足 SLO 的最便宜方案。解析模型筛选、模拟器验证;SLO 是 P90 就要用 P90 的长度检查;模拟要跑到稳态;整数台在流量小时很重要;价格一变,答案就变。
  4. 做项目:从瓶颈、负载、硬件变化或评测问题出发;强而可比的基线;严谨的测量(交替、置信区间、独立报告、稳态、正确性);模型和实测互相校准;消融和敏感性;诚实地写局限和意外。
  5. 开放问题:内存墙、长上下文、智能体负载、异构解耦、低精度、内核与编译器、评测、能耗。

6. 常见误区 · Common pitfalls

  • “把每一讲的加速比乘起来,就是端到端的加速比。” 各层共享显存、算力和带宽,收益会互相抵消(批处理让投机解码的收益变小);要端到端地测或模拟。
  • “解析模型说够了,就够了。” 它对长尾和排队常常偏乐观(写代码的智能体:解析容量是模拟的 1/0.69 倍),也可能偏保守(离线批处理)。
  • “用平均长度检查 P90 的 SLO。” P90 由长请求决定;用平均值会把根本不可行的方案选出来。
  • “模拟了 1,500 个请求,结果很稳定。” 稳定可能只是因为队列还没长起来;过载要很久才看得出来。
  • “最快的 GPU 一定最省钱。” 取决于价格和流量;流量小时整数台的粒度可以抹平效率的差别。
  • “FP8 总是可以用。” 那是质量问题,规划器只把它当作约束;要在你的任务上量。
  • “解耦总是更好 / 总是更差。” 本讲里它只在 TPOT ≤ 10 ms 时胜出;第 5 讲里它在 TTFT 很紧时胜出。答案取决于 SLO 和负载。
  • “项目的价值在于新方法。” 一个严谨的测量、一个推翻常识的发现、一个更可信的评测,同样是好项目。

7. 练习 · Exercises

练习 1(批处理为什么没有 64 倍) 从第 2 级(批 1,每块 GPU 15 token/s)到第 3 级(批 64,591 token/s)只提高了约 40 倍,不是 64 倍。为什么?

答案:一步的时间从 8.48 ms 涨到 13.53 ms:读 KV 从 0.06 涨到 4.01 ms(64 个序列 × 4096 个 token),计算从 0.03 涨到 2.02 ms,通信从 0.98 涨到 2.08 ms。吞吐 ∝ 批 / 一步 = 64 × 8.48 / 13.53 ≈ 40 倍。

练习 2(成本公式) 2 块 GPU、每块 4 美元 / 小时,服务 20 请求 / 秒、每个请求平均 250 个输出 token。每百万输出 token 多少钱?

答案:2×4/(20×250×3600)×106=0.4442 \times 4 / (20 \times 250 \times 3600) \times 10^6 = 0.444 美元(和 02 演示 1 一致)。

练习 3(TPOT 的下界) Llama-3.1-70B FP8(矩阵乘法参数约 69.5 B,每个 1 字节),H100 有效带宽按 3.35 TB/s × 0.8 算。单卡 decode 一步至少要多久?TPOT ≤ 10 ms 至少要几路张量并行?

答案:69.5×109/(2.68×1012)≈2669.5 \times 10^9 / (2.68 \times 10^{12}) \approx 26 ms,单卡不可能;TP4 时约 6.5 ms(还要加 KV、通信、固定开销),TP4 起才有可能——这正是 04 演示 1 里 decode 用 TP4 的原因。

练习 4(prefill 的利用率) 单个请求的 prefill S=0.12S = 0.12 s,平均每个请求占 Sb=0.12S_b = 0.12 s,TTFT ≤ 1 s,尾部系数 2.3。按 planner 的公式最大利用率是多少?

答案:a=1−0.12=0.88a = 1 - 0.12 = 0.88;ρ≤a/(a+2.3Sb/2)=0.88/(0.88+0.138)≈0.86\rho \le a / (a + 2.3 S_b / 2) = 0.88 / (0.88 + 0.138) \approx 0.86(02 演示 1 里 H100 单卡 prefill 实例的 0.85–0.87)。

练习 5(P90 的长度) 提示长度对数正态、平均 8000、σ = 0.8。P90 是多少?

答案:μ=ln⁡8000−0.82/2≈8.667\mu = \ln 8000 - 0.8^2 / 2 \approx 8.667;P90 =eμ+1.2816×0.8≈16,195= e^{\mu + 1.2816 \times 0.8} \approx 16{,}195。

练习 6(整数台) 每个实例能承受 16.2 请求 / 秒,目标 20 请求 / 秒。要几个实例?平均利用率多少?

答案:⌈20/16.2⌉=2\lceil 20 / 16.2 \rceil = 2 个,利用率 20/32.4≈62%20 / 32.4 \approx 62\%。

练习 7(为什么模拟要长) 一个系统的到达率比容量高 10%。粗略地说,队列里的等待时间每过 1 秒增长多少?TTFT 限制 60 秒时,大约要模拟多久才能发现它不稳定?

答案:到达率 λ = 1.1μ,积压每秒增加 λ − μ = 0.1μ 个请求;按 μ 的速度处理,等待时间每秒增加约 0.1 s,约 600 秒后等待才超过 60 秒。只模拟十几秒(1,500 个请求)根本看不出来——§2.3 的表正是这样。

练习 8(解耦什么时候赢) 为什么 TPOT ≤ 10 ms 时解耦胜出,而 50 ms 时共置更便宜?

答案:10 ms 时 decode 要 TP4 才够快(练习 3);共置只能用一种并行方式,prefill 也只好跑在 TP4 上,多出来的 GPU 和通信是浪费;解耦让 prefill 用单卡、decode 用 TP4,各取所需。50 ms 时单卡(或 TP2)就能满足 TPOT,共置的分块 prefill 已经控制住了干扰,解耦反而要多付 KV 传输和两边取整的代价。

练习 9(投机解码的账) α = 0.8、γ = 4,一轮平均产出几个 token?

答案:(1−0.85)/(1−0.8)=3.36(1 - 0.8^5) / (1 - 0.8) = 3.36(第 6 讲的式 1)。

练习 10(能量) 8 块 H100(每块 700 W)、每块 GPU 每秒 1,261 个 token。每个 token 多少焦耳?

答案:700×8/(1,261×8)≈0.56700 \times 8 / (1{,}261 \times 8) \approx 0.56 J(01 演示 2 的第 5 级)。

练习 11(设计一个测量) 你要比较两个 decode 内核 A、B 的速度,怎样设计实验,才不会被噪声、cache 和挑赢家骗?

答案(要点):固定输入大小(足够大)和 cache 状态(例如每次前清 L2);预热;A B 交替测 30 次以上,报告中位数和 bootstrap 置信区间;如果是从很多候选里挑出来的,用一组新的测量重新报告;检查两者输出一致(容差、多种输入分布);写明硬件、驱动、编译器版本。

练习 12(选一个项目) 从 §3.7 里挑一个点子,写出:要回答的问题、基线、指标、最关键的一个实验、你预期的结果和”如果预期错了说明什么”。

答案:开放题。检查标准:问题是否具体到一个学期能回答;基线是否强且可比;指标是否和问题直接对应(例如”同样 SLO 下每百万 token 的成本”);实验是否能区分”你的解释”和”别的解释”。


8. 延伸阅读 · Further reading

回到起点

  • J. Austin et al., How to Scale Your Model(Google DeepMind,2025):第 0–3、7 章是第 1、4 讲的主线。
  • M. Horowitz, Computing’s Energy Problem (and what we can do about it), ISSCC 2014:第 1 讲的能量表。

部署与评测

  • Y. Zhong et al., DistServe, OSDI 2024(第 5 讲):解析筛选 + 模拟验证的放置算法。
  • R. Pope et al., Efficiently Scaling Transformer Inference, MLSys 2023(第 4 讲):延迟、吞吐与成本的前沿。
  • CS349D 第 17 讲:一个迷你推理引擎的端到端评测,以及怎样评测一个推理系统(稳态、开环与闭环、公共随机数、调好基线)。

本仓库

  • 每一讲代码目录的 README 都有”关于实验结果(写作过程中和预想不一致的地方)“一节——它们是这门课里最接近”做研究”的部分。
  • SYLLABUS:全部讲次的大纲、和原课的对应、写作约定。

9. 结语 · Closing

这门课从”读 8 个字节比算一次乘法贵几百倍”出发,一路往下走:模型的账本、精度、切分、服务系统、KV 管理、硬件、内核、编译器,最后到”让 AI 来写内核”和”为智能体做推理”。每一层看到的都是同一个问题的不同侧面——把数据搬到算力旁边的代价——和同一种思维方式:先算账(roofline、能量、成本),再动手,最后诚实地测。希望你带走的不是某一个技巧,而是这种思维方式。

← 返回课程目录