Chapter 9

推理优化

Inference Optimization

作者:Chip Huyen 图 19 张 · 表 3 张
章引言

让模型更快、更便宜

Introduction

新模型不断出现又消失,但让模型更好、更便宜、更快永远重要。前面章节讨论怎样变得更好,本章聚焦更快、更便宜。

模型再好,太慢会让用户失去耐心,甚至让预测失去意义——若预测明日股价却要两天才能算完,就毫无用处。模型太贵,投资回报也不值得。

优化可在模型、硬件、服务三个层级进行。模型层可缩小训练后模型,或设计更高效架构,例如消除 Transformer 注意力瓶颈;硬件层可设计更强芯片;推理服务在给定硬件上运行模型,处理请求,还可针对硬件优化模型,并结合用量、流量分配资源来降低延迟与成本。

因此推理优化是一项跨学科工作,模型研究员、应用开发者、系统工程师、编译器设计者、硬件架构师乃至数据中心运营者经常共同参与。

本章讨论 AI 推理瓶颈及解决技术,重点是模型和服务层,同时概览 AI 加速器,也讨论性能指标及权衡。有时加速同时降成本,例如降低精度让模型更小、更快;更多时候必须取舍,例如最好硬件更快却更贵。

开源模型越来越多,更多团队自建推理服务。即使不亲自实现,理解技术也有助评价服务和框架。应用延迟或成本正在造成问题时,本章可帮助诊断原因和潜在解法。

第一部分

理解推理优化

Understanding Inference Optimization

AI 模型生命周期有两个阶段:训练是构建模型,推理是用模型为给定输入计算输出。除非亲自训练或微调,主要关心的就是推理。本节先建立全章共用词汇,熟悉者可跳到感兴趣部分。

推理概览

Inference Overview

生产中运行模型推理的组件叫推理服务器。它托管可用模型并连接所需硬件,根据应用请求(例如用户提示)分配资源执行相应模型,再把回答返回用户。

推理服务器属于更广的推理服务;服务还负责接收、路由并可能预处理请求,然后才送到服务器。简单架构见图 9-1。

简单推理服务架构
图 9-1 一个简单的推理服务。

OpenAI、Google 的模型 API 都是推理服务。使用它们时无需实现本章大多数技术;自行托管模型时,则要自己构建、优化、维护服务。

计算瓶颈

Computational Bottlenecks

优化就是识别并解决瓶颈。城市交通优化要找拥堵点,推理服务器也要针对工作负载瓶颈设计。主要有两类:

计算受限(compute-bound)

完成时间由所需计算决定。例如密码解密需要密集数学运算,通常计算受限。

内存带宽受限(memory bandwidth-bound)

受系统内数据传输速率限制,例如数据在 CPU 内存、训练在 GPU 时,CPU→GPU 传输可能很久。文献常简称 memory-bound。

术语歧义:内存受限与带宽受限

有人用 memory-bound 指容量不足,而非带宽不足,例如硬件装不下整个互联网,会出现工程师熟悉的 OOM。

容量不足通常可拆分任务缓解。例如 GPU 放不下完整模型,就把模型拆到 GPU 与 CPU;计算会因传输变慢,但传输足够快时问题会减轻。因此容量限制最后往往仍表现为带宽问题。

Williams 等人的 Roofline 论文(2009)用算术强度——每访问一个字节内存执行多少算术运算——区分两类瓶颈。NVIDIA Nsight 等分析工具会给出屋顶线图,见图 9-2。

计算受限可通过分散到更多芯片或使用更高 FLOP/s 的芯片加速;带宽受限则可使用更高带宽芯片。

用屋顶线图区分计算受限与带宽受限
图 9-2 屋顶线图帮助判断操作是计算受限还是内存带宽受限;坐标采用对数尺度。

架构和负载不同,瓶颈也不同。Stable Diffusion 等图像生成推理通常计算受限,自回归语言模型通常带宽受限。

Transformer 语言模型推理有两步:

预填充(prefill)

并行处理输入词元;一次能处理多少受硬件运算数限制,因此计算受限。

解码(decode)

逐个生成输出词元。高层上要把模型权重等大矩阵载入 GPU,受内存加载速度限制,因此带宽受限。

自回归语言模型预填充和解码两个推理步骤
图 9-3 自回归语言模型推理包含预填充和解码;<eos> 表示序列结束词元。

两步计算特征不同,生产中常部署在不同机器,本章服务优化部分会讨论。上下文长度、输出长度、请求批处理策略决定各自计算量和瓶颈。长上下文通常使负载带宽受限,但聪明优化可以消除。

由于 Transformer 流行而现有加速器有限,许多 AI 与数据负载目前带宽受限;未来软硬件进步可能让它们转为计算受限。

在线与批量推理 API

Online and Batch Inference APIs

供应商常提供两类 API:

  • 在线 API:优化延迟,请求到达立即处理。
  • 批量 API:优化成本。无严格延迟要求时,可用批量请求、高延迟和更便宜硬件换取效率。本书写作时 Gemini、OpenAI 批量 API 都便宜 50%,但完成时间从秒/分钟变成小时级。

在线 API 仍可在不显著影响延迟时批处理;真正差异只是在线追求低延迟,批量追求高吞吐。

聊天、代码生成等面向客户场景通常用在线 API。适合批量 API 的包括:

  • 生成合成数据;
  • 定期报告,例如 Slack 摘要、社交媒体品牌情感、客服工单分析;
  • 新客户入驻时处理其全部上传文档;
  • 迁移新模型时重处理所有数据;
  • 为大量客户生成个性化推荐或简报;
  • 重建组织数据索引以更新知识库。

API 默认返回完整回答,但自回归解码可能很久,用户没有耐心。很多在线 API 提供流式模式,生成一个词元就返回一个,缩短首次等待。缺点是无法在展示前给完整回答评分,用户更可能看到坏内容;风险被检测到后仍可追溯更新或删除。

警告:基础模型批量 API 与传统 ML 批量推理不同

传统 ML 中,在线推理是请求到达后计算;批量推理是请求前预计算。推荐系统输入有限且可预测,可提前为全部用户生成推荐,再在用户访问时取出。基础模型输入开放,很难预先预测所有提示。

第二部分

推理性能指标

Inference Performance Metrics

优化前要知道优化什么。用户视角的核心是延迟(回答质量属于模型属性,不属于服务属性),应用开发者还要考虑吞吐与利用率,因为它们决定成本。

延迟、TTFT 与 TPOT

Latency, TTFT, and TPOT

延迟是从用户发送查询到收到完整回答的时间。自回归生成尤其在流式模式下可拆成:

首词元时间(TTFT)

查询后生成第一个词元需要多久,对应预填充时长并取决于输入长度。不同应用预期不同:对话机器人应近乎即时,长文档摘要则可等待更久。

每输出词元时间(TPOT)

首词元后,每个输出词元生成多快。每词元 100 ms 时,1,000 词元回答要 100 秒。流式阅读时只需快于人类阅读速度;很快的读者约 120 ms/词元,因此 120 ms、即每秒 6~8 词元足以满足多数场景。

词元间时间(TBT/ITL)

都衡量相邻输出词元间的时间。

总延迟 = TTFT + TPOT × 输出词元数

总延迟相同的应用也可能体验不同:用户偏爱第一个词元立即出现、之后慢一些,还是稍等首词元、之后快速输出?需要用户研究。可通过在解码与预填充间移动计算实例,牺牲 TPOT 降 TTFT,反之亦然。

用户观察的 TTFT 可能不同于模型,CoT 或智能体尤其如此,因为中间步骤不展示。有些团队改用发布首词元时间明确衡量用户真正看到的首词元。假设模型先生成不可见计划、执行动作并记录不可见输出,最后才生成展示回答;模型在第一步就开始生成,用户却到第三步才看到,因此用户 TTFT 长得多。

延迟是分布,平均值会误导。10 个 TTFT 中九个约 100 ms、一个 3,000 ms,平均会变成 390 ms,让服务看起来普遍很慢。异常可能来自网络错误或特别长的提示,仍应调查。请求量大时,离群点几乎不可避免。

百分位更有帮助。p50 即中位数;p50=100 ms 表示一半请求比它长、一半比它短。常看 p90、p95、p99 来发现异常,也应把 TTFT 对输入长度作图。

吞吐量与有效吞吐量

Throughput and Goodput

吞吐量衡量推理服务跨全部用户和请求每秒生成多少输出词元。有团队把输入输出都算进去,但预填充与解码瓶颈不同、现代服务器还常解耦,所以应分别计数;不加限定的 throughput 通常指输出。

常用单位是 tokens/s(TPS)。多用户时也用 tokens/s/user 判断扩展性。还可按一定时间内完成的请求数衡量,如 RPS;基础模型请求常需数秒,所以很多人用每分钟完成请求数 RPM。它有助了解并发处理;有些供应商会在并发过高时限流。

吞吐直接关联算力成本,越高通常越便宜。若硬件 $2/小时、吞吐 100 tokens/s,则每百万输出词元约 $5.556。每请求平均生成 200 词元,解码 1,000 个请求约 $1.11。

预填充类似计算:硬件 $2/小时、每分钟预填充 100 请求,则 1,000 请求约 $0.33。总成本是两者之和,即 $1.44/千请求。

好吞吐取决于模型、硬件、负载。小模型和高端芯片通常更高;输入输出长度一致的负载比长度多变的易优化。即使规模相近,直接比较也只是近似,因为模型分词器不同;按每请求成本评价服务器效率更好。

AI 应用也有延迟—吞吐权衡。批处理可提高吞吐,却增加延迟。LinkedIn 团队说,愿意牺牲 TTFT、TPOT 时,吞吐翻两三倍并不少见。

只按吞吐和成本优化会损害体验,因此有团队采用从网络领域引入的有效吞吐量(goodput):每秒满足软件级目标 SLO 的请求数。若要求 TTFT≤200 ms、TPOT≤100 ms,服务每分钟完成 100 请求但只有 30 个达标,有效吞吐就是 30 RPM。

吞吐10RPS但仅3RPS满足SLO时有效吞吐为3RPS
图 9-4 服务可完成 10 RPS,但只有 3 个满足 SLO,则有效吞吐为 3 RPS。

利用率、MFU 与 MBU

Utilization, MFU, and MBU

利用率衡量资源使用效率,即活跃使用量占总容量的比例。常被误解的是 GPU utilization。NVIDIA nvidia-smi 显示的是 GPU 积极处理任务的时间比例:十小时中工作五小时就是 50%。

但“正在处理”不代表高效。一个每秒能做 100 次运算的微型 GPU,即使只做 1 次,也可能报告 100%。真正关心的是机器全部可用运算中实际做了多少,即 MFU(Model FLOP/s Utilization)

MFU 是观察吞吐相对峰值 FLOP/s 理论最大吞吐的比例。芯片理论能生成 100 tokens/s,实际服务只有 20,则 MFU=20%。

内存带宽同样昂贵,MBU(Model Bandwidth Utilization)衡量实际使用的可达带宽比例。峰值 1 TB/s、推理用 500 GB/s,则 MBU=50%。

推理已用带宽 = 参数量 × 每参数字节数 × tokens/s

MBU = 已用带宽 ÷ 理论峰值带宽

70 亿参数 FP16 模型(每参数 2 字节)达到 100 tokens/s,使用带宽: 7B × 2 × 100 = 700 GB/s。这说明量化很重要:每参数字节越少,占用宝贵带宽越少。在峰值 2 TB/s 的 A100-80GB 上,MBU=700 GB/s ÷ 2 TB/s = 70%。

吞吐与 MFU/MBU 都是线性关系,有人会用吞吐代称。怎样算好取决于模型、硬件、负载。计算受限通常 MFU 高、MBU 低;带宽受限相反。

训练负载更可预测、批处理更高效,MFU 通常高于推理。推理中预填充 MFU 高于解码。本书写作时训练 MFU 超过 50% 通常算好,但在特定硬件上很难达到。表 9-1 给出示例。

模型参数量加速器MFU
GPT-3175BV10021.3%
Gopher280B4096 TPU v332.5%
Megatron-Turing NLG530B2240 A10030.2%
PaLM540B6144 TPU v446.2%

表 9-1 PaLM 论文中的 MFU 示例(Chowdhery 等,2022)。

图 9-5 展示 FP16 Llama 2-70B 在不同硬件上的 MBU。并发用户增加时 MBU 下降,可能因为每秒计算负担上升,让负载从带宽受限转向计算受限。

并发用户增加时Llama2-70B带宽利用率下降
图 9-5 Llama 2-70B FP16 在三种芯片上随并发用户增加而 MBU 下降。图片来自 Databricks(2024)的 Intel Gaudi 2 研究。

利用率适合追踪同硬件、相似负载下服务是否更高效,但目标不是选择利用率最高的芯片,而是更快、更便宜完成工作。如果成本和延迟都上升,高利用率毫无意义。

第三部分

AI 加速器

AI Accelerators

软件能多快、多便宜取决于硬件。虽然有跨硬件优化,理解硬件能进一步深入。本节从推理视角介绍,也适用于训练。

AI 模型和硬件发展一直交织。计算机不够强是 1970 年代第一次 AI 寒冬的原因之一;2012 年深度学习复兴也与算力相关。AlexNet 广受关注的一项原因是首批成功使用 GPU 训练神经网络的论文之一。此前同规模模型要用数千 CPU;几块 GPU 对博士生和研究者可得得多,触发研究繁荣。

加速器是为加速特定计算负载设计的芯片,AI 加速器针对 AI。主导类型是 GPU,2020 年代初 AI 热潮的最大经济赢家无疑是 NVIDIA。

CPU 面向通用用途,GPU 面向并行处理。CPU 只有少量强核,高端消费设备通常不超过 64 核,适合操作系统、I/O、复杂顺序过程等高单线程任务;GPU 有数千个更小的核,适合拆成大量独立计算的图形渲染和 ML。ML 最主要操作是高度可并行的矩阵乘法。

高效并行提高计算能力,也给内存设计和功耗带来挑战。NVIDIA 的成功催生 AMD 新 GPU、Google TPU、Intel Habana Gaudi、Graphcore IPU、Groq LPU、Cerebras QPU 等。

很多芯片兼顾训练和推理,但专用推理芯片正成为趋势。调查显示,部署系统的推理可超过训练成本,并占 ML 成本最多 90%。训练因反向传播需要更多内存、也更难低精度;训练强调吞吐,推理强调延迟。所以推理芯片常优化低精度和快速内存访问,而非大容量。例子有 Apple Neural Engine、AWS Inferentia、Meta MTIA,以及 Google Edge TPU、NVIDIA Jetson Xavier 等边缘芯片。

还有为 Transformer 等特定架构优化的芯片;越来越多芯片从数据中心走向手机和笔记本。硬件架构有不同内存布局和专用计算单元,针对标量、向量、张量等类型,见图 9-6。

标量向量矩阵张量等不同计算原语
图 9-6 不同计算原语。灵感来自 Chen 等人(2018)。

一块芯片可混合多类单元。GPU 传统支持向量运算,现代 GPU 加入针对矩阵和张量的 Tensor Core;TPU 则从一开始就以张量为主要原语。要高效运行模型,必须考虑其内存布局和原语。

计算能力、内存与功耗

Computational Capabilities, Memory, and Power

计算能力

通常用单位时间运算数衡量,最常见是 FLOP/s(常写 FLOPS),表示每秒浮点运算峰值。应用几乎不可能达到峰值;实际与理论之比就是一种利用率。

每秒运算数取决于数值精度:精度越高,能做的运算越少。两个 32 位数相加通常比 16 位数多约一倍计算,但不同芯片优化使比例不一定正好一半。表 9-2 是 NVIDIA H100 SXM 的规格。

数值精度稀疏条件下 teraFLOP/s
TF32 Tensor Core989
BFLOAT16 Tensor Core1,979
FP16 Tensor Core1,979
FP8 Tensor Core3,958

表 9-2 NVIDIA H100 SXM 的 FLOP/s 规格。TF32 实际是 19 位格式。

内存容量与带宽

GPU 大量核心并行工作,数据必须不断从内存移到核心。AI 权重矩阵和训练数据很大,必须快速移动才能让核心忙碌。GPU 内存要比 CPU 内存带宽更高、延迟更低,因此技术更先进、成本也更高。

CPU 通常用二维结构 DDR SDRAM,高端 GPU 常用三维堆叠 HBM。加速器通常与三级内存交互:

  • CPU DRAM:系统/主机内存,带宽最低,约 25~50 GB/s;普通笔记本 16~64 GB,高端工作站可达 1 TB。
  • GPU HBM:靠近 GPU 的专用内存,约 256 GB/s 到超过 1.5 TB/s;消费 GPU 常有 24~80 GB。
  • 片上 SRAM:芯片内部的 L1/L2(有时 L3)缓存、寄存器和共享内存,常超过 10 TB/s,但容量通常不超过 40 MB。
AI加速器的CPU内存HBM和片上SRAM层次
图 9-7 AI 加速器的内存层次;数值仅供参考,实际因芯片而异。

很多 GPU 优化都围绕如何利用内存层次。PyTorch、TensorFlow 目前不能细粒度控制访问,因此更多工程师学习 CUDA、OpenAI Triton、AMD 开源 ROCm 等 GPU 编程语言。

功耗

芯片靠晶体管开关完成计算,每次切换都耗能。A100 有 540 亿晶体管,H100 有 800 亿;高效运行时数十亿晶体管快速切换,消耗大量能量并产热,冷却又继续用电。

能耗给环境带来巨大压力。H100 满负载运行一年约耗 7,000 kWh,而美国家庭年均约 10,000 kWh,所以电力已成为扩展算力的瓶颈。

规格常写最大功耗或 TDP。最大功耗是满载峰值;TDP 是典型负载下冷却系统需散去的最大热量,不等于准确功耗,但可作为预期指标。CPU/GPU 最大功耗约为 TDP 的 1.1~1.5 倍,依架构与负载而变。

使用云服务无需自行处理冷却和供电,但这些数字仍能帮助理解环境影响与电力需求。

选择加速器

计算受限负载应关注更高 FLOP/s;内存受限负载则值得购买更高带宽与更大容量。评估时问三件事:能否运行?要多久?多少钱?FLOP/s、容量、带宽回答前两项;第三项根据云端使用费,或购置成本加持续电费计算。

第四部分

推理优化

Inference Optimization

优化可在模型、硬件、服务层进行。用射箭比喻:模型优化是打造更好的箭,硬件优化是训练更强弓箭手,服务优化是完善从弓到瞄准条件的整个流程。

理想情况下,速度与成本优化不改变质量;实际很多技术会导致退化。图 9-8 展示同一 Llama 由不同推理供应商托管时,在基准上略有质量差异。

不同推理供应商优化可能导致相同模型质量差异
图 9-8 推理供应商的优化可能改变模型行为,使同一模型出现轻微质量差异。实验来自 Cerebras(2024)。

硬件设计超出本书范围,下面讨论模型与服务层。生产优化通常会组合多个层级的技术。

第五部分

模型优化

Model Optimization

模型层优化常通过修改模型来提高效率,因此可能改变行为。当前许多基础模型采用 Transformer 并含自回归语言模型组件,三项特征让推理耗资源:模型规模、自回归解码、注意力机制。

模型压缩

Model Compression

模型压缩缩小模型,通常也让它更快。本书已介绍两种:第 7 章的量化降低精度以减少内存、提高吞吐;第 8 章的蒸馏训练小模型模仿大模型。

蒸馏暗示大模型行为可由较少参数捕捉。那么大模型内部是否存在一个参数子集,足以捕捉全部行为?这就是剪枝核心。

神经网络剪枝有两种含义:删除整个节点,改变架构并减少参数总数;或找到对预测最无用的参数并置零。后一种不减少参数总数,只减少非零参数,让模型稀疏,从而减少存储并加速。

剪枝后可直接使用,也可继续微调剩余参数,恢复性能。剪枝还能发现有希望的小架构,再从零训练。文献结果很鼓舞:Frankle 与 Carbin(2019)把某些网络非零参数减少 90% 以上,不损害准确率并改善速度。但目前实践中较少见,因为需要理解原架构、收益常不如其他方法,而且硬件未必能利用稀疏性。

仅权重量化最流行:易用、开箱支持多模型、效果明显。32 位降到 16 位,内存减半;但量化接近极限,不可能低于每值 1 位。蒸馏也常见,因为能得到满足需求、行为接近大模型的小模型。

自回归模型逐词元生成。每词元 100 ms 时,100 词元要 10 秒,不只慢还贵;API 输出词元价格约为输入词元 2~4 倍。Anyscale 实验发现一个输出词元对延迟的影响相当于 100 个输入词元。稍微改善解码就能显著改善体验。

领域演进很快,新方法不断尝试突破顺序生成瓶颈,未来也许会有从架构上消除瓶颈的模型。下面技术展示可能的方向。

推测解码

Speculative Decoding

推测解码(推测采样)让更快但较弱的草稿/提议模型先生成一串词元,再由真正要使用的目标模型验证。

给定输入词元 x1, x2, …, xt

  1. 草稿模型生成 K 个词元:xt+1 … xt+K
  2. 目标模型并行验证这 K 个词元;
  3. 从左到右接受目标模型同意使用的最长草稿子序列;
  4. 若接受 j 个,再由目标模型多生成一个 xt+j+1,随后循环。

若一个草稿都不接受,这轮只产生目标模型的一个词元;全部接受则产生 K+1 个。

草稿模型生成K个词元主模型接受最长一致子序列
图 9-9 草稿模型生成 K 个词元,主模型接受与自己一致的最长子序列。来自 Stern 等人(2018)。

若草稿全被拒,目标模型既验证又自己生成,似乎会更慢;但三点洞见避免了这一结果:

  1. 并行验证一串词元比顺序生成快;推测解码把解码计算特征变得像预填充。
  2. 输出中有些词元较容易预测,可找到弱模型准确猜中,取得高接受率。
  3. 解码带宽受限,通常有闲置 FLOP 可“免费”用于验证。

接受率依领域而变,代码等结构化文本通常较高。K 越大,目标模型验证调用越少,但接受率可能越低。草稿模型可用任意架构,理想上与目标共享词表和分词器;可专门训练,也可用现成弱模型。

DeepMind 为 Chinchilla-70B 训练同架构 40 亿参数草稿模型,生成速度是目标的 8 倍(1.8 对 14.1 ms/词元),整体延迟减半以上且不损质量;T5-XXL 也有类似提升。

该方法容易实现且不改变质量,PyTorch 约 50 行代码即可完成,已进入 vLLM、TensorRT-LLM、llama.cpp。

参考式推理

Inference with Reference

回答经常需要复用输入词元。基于附件问答时,模型可能逐字引用文档;修复代码时,大部分原代码不变。与其让模型重新生成,何不直接从输入复制?这就是参考式推理。

它类似推测解码,但草稿不是模型生成,而是从输入中选择。关键是在每步找到上下文最相关文本段,最简单做法是寻找与当前词元匹配的片段。

它无需额外模型,但只适合上下文和输出高度重叠的检索、编码、多轮对话。Yang 等人(2023)在这些场景中实现约 2 倍生成加速。

从输入复制文本片段的参考式推理示例
图 9-10 两个参考式推理示例,成功从输入复制的文本以红色和绿色标出。来自 Yang 等人(2023),采用 CC BY 4.0。

并行解码

Parallel Decoding

另一些技术不使用草稿,而是打破顺序依赖。给定 x1…xt,尝试同时生成 xt+1…xt+k,也就是说还不知道 xt+1 时就预测 xt+2

这可能奏效,因为既有序列经常足以预测接下来多个词。“the cat sits”后即使不知道下一个是 on、under 还是 behind,也可能预测再下一个是 the。

并行词元可由同一解码器产生,如 Lookahead decoding;也可由不同解码头产生,如 Medusa。Medusa 在原模型上增加多个小型神经层,每个头预测特定未来位置。原模型预测 xt+1,第 k 个头预测 xt+k+1;训练头时冻结原模型。NVIDIA 称 Medusa 在 HGX H200 上把 Llama 3.1 生成最高加速 1.9 倍。

非顺序生成的词元必须验证并整合。Lookahead 用 Jacobi 方法:

  1. 并行生成 K 个未来词元;
  2. 验证它们与上下文的一致性、连贯性;
  3. 若部分失败,只重新生成或调整失败词元;
  4. 持续细化直到全部通过并并入输出。

这一族也叫 Jacobi 解码。Medusa 则用树形注意力,每个头为每个位置产生数个选项,组织成树,选择最有希望的组合。

Medusa多个解码头预测词元选项并选择最佳序列
图 9-11 Medusa 每个头为词元位置预测多个选项,再选择最有希望的序列。改编自 Cai 等人(2024),采用 CC BY 4.0。

绕过顺序依赖很有吸引力,但并行解码不直观,Medusa 等技术也较难实现。

注意力机制优化

Attention Mechanism Optimization

第 2 章提到,生成下一词元需要此前全部词元的 key、value 向量。生成 xt+1 时不应再次计算 x1…xt−1,而应复用上一步结果,只计算最新 xt。保存这些向量供复用的缓存叫 KV cache,新向量不断追加。

使用KV缓存复用此前词元的key和value向量
图 9-12 用 KV 缓存复用 key、value 向量,避免每步重新计算。
说明

KV 缓存只用于推理。训练时整段序列已知,下一词元可一次并行计算,不需要顺序生成和 KV 缓存。

生成词元要与全部此前词元计算注意力,计算量按序列长度 O(n²) 增长;KV 缓存大小则线性增长,也随批量增大。Google 计算,500B+ 多头注意力模型在批量 512、上下文 2048 时,KV 缓存达 3 TB,是权重大小三倍。

缓存最终受硬件容量限制,成为长上下文瓶颈;载入大缓存也耗时,不利于严格延迟。注意力的计算和内存需求正是长上下文困难的原因。技术主要分三类:重设计注意力、优化 KV 缓存、编写注意力内核。

计算 KV 缓存大小

KV 内存 = 2 × B × S × L × H × M

  • B:批量大小;S:序列长度;L:Transformer 层数;
  • H:模型维度;M:数值表示每值所需字节(如 FP16/FP32)。

Llama 2 13B 有 40 层、维度 5,120。B=32、S=2,048、每值 2 字节时: 2 × 32 × 2,048 × 40 × 5,120 × 2 = 54 GB。

重设计注意力机制

这会直接改变架构,只能训练或微调时采用。局部窗口注意力生成新词元时只关注固定大小的附近窗口,减少有效序列长度、缓存和计算。平均 10,000 词元、窗口 1,000 时,KV 缓存缩小 10 倍。局部注意力可与全局注意力交错:前者捕捉近邻上下文,后者捕捉跨文档任务信息。

跨层注意力在相邻层共享 key/value;三层共享可缩小三倍。多查询注意力在多个 query head 间共享 key/value。分组查询注意力是更灵活推广:把 query head 分组,仅组内共享,平衡头数量与 key/value 数量。

Character.AI 平均对话历史有 180 条消息,KV 缓存是吞吐主要瓶颈。多查询、局部/全局交错、跨层三种设计让缓存缩小 20 倍以上,使大批量服务不再受内存限制。

优化 KV 缓存

缓存管理对缓解内存瓶颈、扩大批量尤其重要。vLLM 因 PagedAttention 流行:把缓存切成不连续块,减少碎片并灵活共享内存。其他方法包括 KV cache 量化、自适应压缩和选择性缓存。

为注意力计算编写内核

不改机制或存储,而优化注意力分数的计算方式;结合执行硬件时最有效。针对特定芯片的优化代码叫内核。著名的 FlashAttention 把 Transformer 中多个常用操作融合起来加速。

FlashAttention融合多个常用算子
图 9-13 FlashAttention 内核把多个常用算子融合在一起。改编自采用 BSD 3-Clause 的原图。

内核与编译器

Kernels and Compilers

内核是针对 GPU/TPU 等硬件优化的专门代码,执行需要反复、并行运行的计算密集例程。矩阵乘法、注意力、卷积都有专门内核。

编写内核要深入理解缓存、全局/共享内存、寄存器等层次和数据移动,通常用 CUDA、Triton、ROCm 等低层语言。它们能细粒度控制线程和访问,却比 Python 难学。

过去内核像少数人的黑魔法。NVIDIA、AMD 聘优化工程师为自家硬件写内核;PyTorch、TensorFlow 聘内核工程师适配加速器。推理优化需求上升后,更多 AI 工程师开始学习。四种常见技术:

  • 向量化:循环中同时处理内存连续的多个元素,减少数据 I/O 和延迟。
  • 并行化:把数组/高维数组拆成独立块,在不同核或线程同时处理。
  • 循环分块:按硬件内存布局和缓存优化访问顺序;CPU 高效模式未必适合 GPU。
  • 算子融合:把多个算子合成一次遍历,避免重复内存访问;例如两个循环处理同一数组时合并。

前三者广泛适用,算子融合需要更了解具体模型算子和架构。硬件更新后必须开发新内核;FlashAttention 最初面向 A100,后来 FlashAttention-3 面向 H100。

模型脚本规定要执行的操作序列,运行到 GPU 前要转换成硬件兼容语言,这叫降低(lowering);完成转换的工具叫编译器。编译器连接模型和硬件,降低时尽量把操作转换成专用内核。

PyTorch 推理优化案例

PyTorch 团队按以下步骤提高 Llama-7B 吞吐:

  1. 调用 torch.compile 编译成更高效内核;
  2. 把权重量化到 INT8;
  3. 继续量化到 INT4;
  4. 加入推测解码。
PyTorch不同优化技术带来的吞吐提升
图 9-14 PyTorch 中不同优化技术带来的吞吐提升。来自 PyTorch(2023)。

实验在 80 GB A100 上运行,没有说明对输出质量的影响。编译器可以独立存在,如 Apache TVM、MLIR;也可集成在框架中,如 torch.compile、XLA/OpenXLA、针对 NVIDIA 的 TensorRT 编译器。AI 公司还可能拥有专有编译器和内核,以加速自家负载。

第六部分

推理服务优化

Inference Service Optimization

服务层技术大多聚焦资源管理:给定固定计算与内存,以及由不同模型、不同用户请求组成的动态负载,高效分配资源以优化延迟和成本。与很多模型层技术不同,它们不修改模型,理论上不改变输出质量。

批处理

Batching

降低成本最简单的方法之一是批处理。生产服务可能同时收到多个请求;把相近时间到达的请求合在一起,能显著提高吞吐。单独处理像人人开车,批处理像坐公交;公交能运更多人,也可能让个人旅程变长,但智能调度可把延迟影响降到最低。

三种主要技术是静态、动态、连续批处理。

静态批处理

把固定数量输入组成批次,像坐满才发车的公交。所有请求必须等批次满,首个请求会等到最后一个到达,不管它多晚。

动态批处理

为每批设最大时间窗口。批大小 4、窗口 100 ms 时,收到 4 个或经过 100 ms 就处理,先到者为准,像按时或坐满就发车。延迟受控,但批次可能没满,浪费计算。

静态批处理和动态批处理对延迟及计算效率的影响
图 9-15 动态批处理让延迟可控,但计算效率可能较低。

连续批处理

朴素批处理要等一批所有请求结束才返回。LLM 请求长度差异很大:10 词元短回答若和 1,000 词元回答同批,会无谓等待。

连续批处理让完成的回答立即返回,通过只合并不会让一个回答阻塞另一个的操作实现(Orca,Yu 等,2022)。一个请求完成并返回后,马上用新请求补位,像公交每放下一名乘客就再载一名,维持占用率。它也叫 in-flight batching。

连续批处理立即返回完成回答并用新请求补位
图 9-16 连续批处理中,完成的回答立即返回,新请求随即补位处理。

解耦预填充与解码

Decoupling Prefill and Decode

预填充计算受限、解码带宽受限,在同一机器执行会低效争抢资源,TTFT 与 TPOT 都变慢。GPU 已接近计算峰值时,也许还能加入低计算量的解码任务;但新查询会同时带来重预填充任务,它可能抽干已有解码的算力,让 TPOT 变慢。

常见技术是把两步分散到不同 GPU。DistServe(Zhong 等,2024)与 Inference Without Interference(Hu 等,2024)显示,对多种 LLM 和应用,这可在满足延迟的同时显著增加请求量。虽然要把中间状态从预填充实例传到解码实例,现代 GPU 集群节点内有 NVLink 等高带宽连接,通信开销并不大。

两类实例比例取决于负载和延迟要求:输入长、优先 TTFT 时可为 2:1~4:1;输入短、优先 TPOT 时可为 1:2~1:1。

提示缓存

Prompt Caching

应用提示经常有重叠文本。提示缓存保存这些片段以复用,只处理一次。最常见重叠是系统提示;无缓存时每次查询都处理,有缓存时首个查询处理一次即可。

长文档查询尤其适合:大量查询围绕同一本书或代码库时,可跨查询复用。长对话也可缓存早期消息,在预测后续消息时复用。提示缓存也叫上下文缓存或前缀缓存。

缓存不同提示中重叠文本片段并复用
图 9-17 提示缓存保存不同提示的重叠片段并复用。

长系统提示应用可显著降延迟和成本。系统提示 1,000 词元、每天一百万次 API 调用,缓存每天省去约十亿个重复输入词元。但并非免费:和 KV 缓存一样会占大量内存;若模型 API 不内置,实现需要显著工程工作。

自 Gim 等人 2023 年 11 月提出后,缓存快速进入 API。写作时 Gemini 缓存输入比普通输入便宜 75%,但存储按每百万词元每小时 $1 收费;Anthropic 宣称最多省 90% 成本、降 75% 延迟,见表 9-3。

用例无缓存 TTFT有缓存 TTFT成本降低
与书对话(缓存 100K 词元)11.5 s2.4 s(−79%)−90%
多样本提示(10K 词元)1.6 s1.1 s(−31%)−86%
多轮对话(10 轮,长系统提示)约 10 s约 2.5 s(−75%)−53%

表 9-3 提示缓存降低的成本与延迟。信息来自 Anthropic(2024)。

并行策略

Parallelism

加速器为并行而设计,并行策略是高性能计算支柱。跨模型通用的两大家族是数据并行和模型并行,LLM 还有上下文与序列并行。一项优化可组合多个策略。

副本并行

最直接:创建多个模型副本。副本越多,并发请求越多,也要更多芯片。把不同规模模型放到不同芯片是装箱问题,模型、副本、芯片越多越复杂。

假设模型有 8B、13B、34B、70B,GPU 内存为 24/40/48/80 GB,全部 8 位。芯片固定时,要决定每模型创建多少副本、放在哪些 GPU,以最大化指标——例如 40 GB GPU 放三个 13B,还是留给一个 34B?副本数固定时则要决定买哪些芯片降低成本,不过这种情况较少见。

模型并行与张量并行

模型太大无法放进一台机器时,要跨机器拆分。推理最常用张量并行,也叫算子内并行。推理是一串高维张量算子,例如矩阵乘法;把一个算子的张量分到多设备,就能把它拆成小块并行执行。矩阵乘法可按列拆一个矩阵。

张量并行既让单机放不下的大模型可服务,也可降延迟;但额外通信会抵消部分收益。

矩阵乘法的张量并行
图 9-18 矩阵乘法中的张量并行。

流水线并行

把模型计算分成阶段,每阶段交给一台设备。数据流过模型时,各阶段重叠处理不同数据,实现并行。

模型各阶段跨设备执行的流水线并行
图 9-19 流水线并行让拆开的模型阶段并行执行。

图 9-19 中,一个批次拆成更小的微批次;某台机器处理完微批次后,把输出传给下一机器上的下一部分模型。

它使多机服务大模型成为可能,但阶段间通信会增加每请求总延迟。严格延迟场景通常偏爱副本并行;训练则常用流水线提高吞吐。

上下文并行与序列并行

两种相对少见方法都为提高长序列处理效率。上下文并行把输入序列拆到设备,例如前半在机器 1、后半在机器 2。序列并行则把整段输入所需的算子拆到机器,例如注意力在机器 1、前馈计算在机器 2。

本章结尾

总结

Summary

模型可用性高度取决于推理成本和延迟。更便宜让 AI 决策更可负担,更快让 AI 能进入更多应用。其潜在影响巨大,吸引大量人才持续提出创新方法。

提高效率前要理解怎样测量。本章从延迟、吞吐、利用率开始。语言模型延迟可拆为受预填充影响的 TTFT 与受解码影响的 TPOT。吞吐直接关联成本,延迟与吞吐存在权衡:愿意增加延迟就可能降成本,而降低延迟常要增加成本。

模型运行效率取决于硬件,因此本章也概览 AI 硬件与针对不同加速器优化的要求。

随后介绍多种推理优化技术。大多数应用开发者会使用模型 API 自带优化,而不是亲自实现;但理解可用技术有助评价 API 效率。

模型层优化常修改模型,可能改变行为;服务层通常保持模型不变,只改变提供方式。模型层通用技术包括量化、蒸馏,特定架构还需专门技术。Transformer 的注意力是关键瓶颈,因此出现 KV 缓存管理和注意力内核;自回归顺序解码也是瓶颈,因此出现推测、参考、并行解码。

服务层包括各种批处理和并行策略,以及自回归专用的预填充/解码解耦与提示缓存。

技术选择取决于负载:长上下文更重视 KV 缓存;有长且重叠提示或多轮对话更重视提示缓存;性能要求也影响选择。若低延迟比成本重要,可扩大副本并行,虽然需要更多机器,但每台处理较少请求,能为单请求分配更多资源。

跨用例最有影响的通常是量化(普遍有效)、张量并行(降延迟且可服务大模型)、副本并行(容易实现)与注意力优化(显著加速 Transformer)。

推理优化结束了本书的模型适配技术清单。下一章将把这些技术整合为完整系统。

第 9 章结束
原书注释

注释

Notes

  1. 第 7 章提到,推理只有前向传播,训练同时有前向和反向传播。
  2. Mark Saroufim 指出训练与推理成本关系:训练总成本 T、每次推理收费 p、可售调用 N 时,只有 T≤p×N 才值得开发。生产使用越多,供应商越能降推理成本;这不适用于在开源模型上转售调用的第三方。
  3. 作者观察,系统背景的人常用 memory-bound 指带宽受限,AI 背景的人常用它指容量受限。
  4. Roofline 论文用 memory-bound 指内存带宽受限。
  5. 预填充实际上是在填充 Transformer 的初始 KV 缓存。
  6. 把服务分为在线/批量能优先保障重要请求。若无延迟退化上限为 X RPS、实际需求 Y>X,理想情况下不紧急请求进入批量,让在线请求先处理。
  7. 应用系统提示常可预知,真正难预测的是用户查询。
  8. 聊天机器人早期有人抱怨回答太快显得不自然;随着用户熟悉,这已不常见。
  9. LinkedIn 使用 TBT,NVIDIA 使用 ITL。
  10. Anyscale 实验显示,100 个输入词元对总延迟的影响约等于一个输出词元。
  11. 人们长期关注 FLOP/s 利用率,但 MFU 一词由 PaLM 论文提出。
  12. 芯片商可能进行“峰值 FLOP/s 黑客”:在稀疏矩阵和特定形状等条件下实验以抬高峰值,使芯片更有吸引力,但用户更难实现高 MFU。
  13. 1960 年代计算机只能运行能力有限的单层神经网络。Minsky 与 Papert 1969 年《Perceptrons》认为隐藏层也做不了更多;当时算力不足以反驳,这一论断后来常被视为 1970 年代 AI 资金枯竭的原因。
  14. GPU 用途早已不只图形,曾有人讨论改名。NVIDIA CEO Jensen Huang 说公司考虑过 GPGPU、XGU,最后相信购买者会理解名字之外的用途。
  15. 矩阵乘法昵称 matmul,据估计占神经网络全部浮点运算 90% 以上。
  16. 芯片可为架构设计,架构也可为芯片设计;Transformer 最初由 Google 设计为在 TPU 上快速运行,后来才为 GPU 优化。
  17. 中低端 GPU 可能使用 GDDR 内存。
  18. 建设数万 GPU 数据中心的一项主要挑战是找到能保证供电的地点;还要权衡供电、速度、地缘政治。偏远地区电便宜,却增加网络延迟,不适合严格延迟推理。
  19. 每个生成步骤都要把完整参数从 HBM 传到计算单元,带宽开销大;一次只生成一个词元,使用 FLOP 很少,计算效率低。
  20. 若 MFU 已达上限,推测解码意义较小。
  21. Jacobi 方法是一种迭代算法,解的多个部分可同时独立更新。
  22. 自回归模型注意力计算数量是 O(n²)。
  23. 卷积常用于 Stable Diffusion 等图像生成模型。
  24. 很多公司把内核视为商业秘密;能比竞争对手更快、更便宜运行模型就是优势。
  25. Meta 2024 年的“Llama Inference at Meta”等演讲提到预填充与解码实例比例。
  26. llama.cpp 也有提示缓存,但写作时似乎只缓存完整提示且仅用于同一聊天。根据代码推测,长对话中它缓存旧消息,只处理最新消息。
  27. 训练中,副本并行的同类技术叫数据并行。