AI Hero Club

大模型高效推理 · 第 1 章

已自行标记完成0 / 8

仅保存在当前浏览器;完成标记不代表通过项目验收。

本章提纲 7 个小节
全课目录 8 章
完整课程计划 ↗
大模型高效推理 / 课程计划 ↗

CHAPTER 01 / 8建议学习 120 分钟

先定义工作负载,再推导推理成本

两个实现都说“每秒更多词元”,怎样确认它们使用了同一工作量与时间分母?

本章要解决的 3 个问题
  • 建立请求时间轴并区分模型计算与用户等待
  • 逐项推导预填充和解码的乘加成本
  • 用单位与反例检查成本估算的适用条件
课程项目 ZIP ↓

项目入口与前置契约

本课围绕一个可复跑的 CPU 推理实验台展开。先修要求是会读取矩阵形状、理解因果注意力和下一词元预测;若这些概念尚不熟悉,应先完成 Transformer 实战前四章。本课不再训练语言能力,而是冻结一台小解码器,比较同一输入在不同执行方式下的输出、空间与耗时。项目随机权重只提供可审计的数值函数,随机词元只提供固定形状的工作负载;它们不能回答中文问答质量好坏。最终交付是带源码、负对照与测量边界的报告。

一次自回归请求先接收长度为 PP 的提示,再输出最多 GG 个词元。预填充将提示一起送入模型,最后位置的 logits 用来选择第一个输出。随后每得到一个新词元,再把它送回模型得到下一个分布。这意味着生成 GG 个输出通常只需 G−1G-1 次后续解码前向;本项目为了比较固定工作量,另定义“给定后续输入轨迹”,预填充后继续处理指定的 DD 个输入词元,并比较 D+1D+1 个预测分布。两种计数不可混用,性能表会明确使用后者。

从时间轴定义指标

令到达、开始计算、首个输出、最终输出时刻依次为 a,s,f,ea,s,f,e,单位统一为秒。排队时间是 s−as-a,服务侧首词元时间是 f−sf-s,用户侧首词元等待是 f−af-a。总响应时间为 e−ae-a。若确有 G>1G>1 个输出,平均输出间隔可定义为 (e−f)/(G−1)(e-f)/(G-1);分母不是提示长度,也不是包括首词元的输出数量。网络传输、分词和格式化是否计入,需要在记录开始点和结束点处说明,否则同名指标仍可能不是同一个量。

吞吐量也有不同分母。某观测区间内完成的输出词元数除以区间秒数,是聚合输出吞吐;把每个请求的输出速率求平均,是请求速率均值。这两个统计量通常不同。只测已经开始执行的批次,会漏掉拥塞时不断增长的队列;只看总吞吐,也会掩盖短请求等长请求的体验。项目第七章只测前向计算,排队和用户首词元延迟均标为未测量,第八章再把可证明的结论与需要服务系统才能判断的结论分开。

乘加账本必须先声明约定

用 BB 表示批量,TT 表示当前序列长度,dd 表示隐藏宽度,ff 表示前馈中间宽度,NN 表示块数,VV 表示词表大小。一项标量乘法加一次加法计作两次浮点运算。矩阵 [m,k][m,k] 乘 [k,n][k,n] 的主项因此为 2mkn2mkn;它忽略每个输出少一次加法的精确差值。下文统计的是密集实现的主项,不包括归一化、偏置、指数、数据移动和 Python 调用开销。这里的近似来自记账口径,不是模型输出近似。

普通多头注意力的 Q、K、V、O 四个投影都是宽度 dd 的线性变换,每块合计 8BTd28BTd^2 次运算。两层前馈分别从 dd 到 ff、从 ff 到 dd,合计 4BTdf4BTdf。多头的打分与加权求和各需 2BT2d2BT^2d,共 4BT2d4BT^2d。项目显式计算完整方阵后施加因果 mask,因此此项按方阵记;若专门的三角内核跳过不可见位置,常数会改变。不能因为上三角被设为负无穷,就认为普通矩阵乘法已经免算了这些位置。

Fprefill≈NB(8Td2+4Tdf+4T2d)+2BTVdF_{\mathrm{prefill}}\approx NB(8Td^2+4Tdf+4T^2d)+2BTVd
预填充的密集乘加主项;最后一项是所有位置的词表投影。

缓存解码只对新位置产生查询和新的 K、V。若加入当前词元后的可见总长度为 tt,单步块内投影与前馈变成 NB(8d2+4df)NB(8d^2+4df),注意力为 4NBtd4NBtd。词表头仅对新位置输出时为 2BVd2BVd。无缓存的参考实现重新计算长度 tt 的全部前缀,因而要把预填充公式中的 TT 换成 tt。缓存没有让新查询免看旧内容,它消除的是旧位置的重复投影、前馈和注意力计算。

Fdecode(t)≈NB(8d2+4df+4td)+2BVdF_{\mathrm{decode}}(t)\approx NB(8d^2+4df+4td)+2BVd
缓存单步的主项;这是有限窗口以内的公式。

完整算例一:同一个小模型的两种执行

取 N=2,B=1,d=32,f=128,V=31,P=12N=2,B=1,d=32,f=128,V=31,P=12。每块投影与前馈的单位置主项为 8×322+4×32×128=245768\times32^2+4\times32\times128=24576。长度十二的预填充每块为 12×24576+4×144×32=31334412\times24576+4\times144\times32=313344,两块再加头部 2×12×31×32=238082\times12\times31\times32=23808,共 650496650496 次运算。在可见长度十三处缓存单步为 2(24576+4×13×32)+1984=544642(24576+4\times13\times32)+1984=54464;整前缀重算则为 2(13×24576+4×169×32)+25792=7080322(13\times24576+4\times169\times32)+25792=708032。这是算术计数差,不是十三倍速度的保证。

为何运算减少很多而实际速度未必同比改善?小矩阵的调用固定成本、张量分配、缓存拼接、解释器调度都可能占显著比例;大模型还受权重与 KV 读写限制。令算术工作量为 FF、最低数据流量为 MM 字节,硬件理想运算率为 RR 运算每秒、带宽为 WW 字节每秒,则执行时间至少受 max⁡(F/R,M/W)\max(F/R,M/W) 约束。这个下界允许计算与传输重叠,不能把两项直接相加当精确时间,也不能把峰值规格当实际达成率。

完整算例二:首词元和吞吐给出相反排序

假设方案甲排队零毫秒、预填充四十毫秒,首输出后九个间隔均为十毫秒;方案乙排队八十毫秒、预填充二十毫秒,九个间隔均为五毫秒。两者都输出十词元,甲的用户首词元等待四十毫秒、完成一百三十毫秒,乙分别一百毫秒、一百四十五毫秒。乙的后续输出更快,用户却更晚拿到首词元和完整答案。如果只报告每秒二百对一百词元,就会丢掉影响体验的队列成本。此处数字是构造反例,不是本机实测。

单位检查是第一类独立验证:时间不得与词元直接相加,乘加量除以运算率才是秒;字节除以字节每秒也才是秒。第二类验证是结构极限:令 T=1T=1 时预填充和单步公式一致,令 G=1G=1 时不应出现后续解码间隔,令 NN 翻倍时块内主项翻倍而头部不翻倍。这些检验不依赖某个运行库。下面用标准库把算例固化为断言,再在后续章节与真实张量及计时交叉对照。

独立运行独立运行的乘加账本,不计运行库开销。
def prefill(T, N=2, B=1, d=32, f=128, V=31):
    return N*B*(8*T*d*d+4*T*d*f+4*T*T*d)+2*B*T*V*d
def decode(t, N=2, B=1, d=32, f=128, V=31):
    return N*B*(8*d*d+4*d*f+4*t*d)+2*B*V*d
assert prefill(12)==650496
assert prefill(13)==708032 and decode(13)==54464
assert prefill(1)==decode(1)
print(prefill(13)/decode(13))

从工作量进一步检查带宽假设

算术强度定义为 I=F/MI=F/M,单位是运算每字节。设一个线性层权重有一百万个元素,每个元素两字节,每个请求的单位置矩阵向量乘约需两百万次运算。若权重每步都从主存读取且没有复用,单请求仅就权重而言强度约为一运算每字节。四个请求在同一批次共享一次读取而执行四份计算时,强度约变成四。这个推导说明批处理可能摊薄权重流量,却没有计算 KV、激活、输出和缓存层级,也没有证明四请求延迟与单请求一样。真实流量应由对应硬件工具另测。

再取一个明确标为假设的设备:有效运算率每秒一百亿次、有效带宽每秒十亿字节。若任务含一亿次运算与两千万字节流量,两个下界分别为十毫秒与二十毫秒,理想重叠的下界是二十毫秒。将运算减半但流量不变,下界仍是二十毫秒;将流量减半则两项同为十毫秒。由此可见,同一个乘加优化在不同瓶颈下可能没有可见收益。项目只测端到端 CPU 前向,未采集实际带宽计数,因此不能声称已经证实该次运行属于某个瓶颈。

检查聚合吞吐还需要闭合观测区间。若请求甲在一秒完成十词元、请求乙在两秒完成十词元,二者同时从零开始,则到第二秒总共完成二十词元,聚合吞吐为每秒十词元;各请求速率的均值却为每秒七点五词元。若漏掉最后一个慢请求,只观察第一秒已完成者,还会制造选择偏差。后文测量表保留全部重复和全部固定后续步,没有以删掉慢样本来提高数字。

练习与项目增量

TRY IT YOURSELF

基础:指标计算

某请求到达0秒,0.03秒开始计算,0.08秒首输出,0.20秒末输出,共7个输出。求排队、用户首词元等待和后续平均间隔。

提示 1
先画到达、开始计算、首输出、末输出四个时间点。
提示 2
排队从到达到计算开始;用户首词元从到达到首输出,后续间隔数比输出数少一。
对照参考解答
排队为0.03秒;用户首词元等待为0.08秒;后续共有6个间隔,平均为(0.20−0.08)/6=0.02秒。若用7作分母会把没有对应间隔的首词元混入。服务侧首词元时间为0.05秒。

TRY IT YOURSELF

进阶:前馈宽度变化

在算例一中只把f从128改为256,长度12的预填充增加多少主项运算?

提示 1
只改变f时,哪些矩阵乘的主项会随它变,哪些不变?
提示 2
对两层前馈的成本公式取差,代入层数、B、T、d和新增宽度,不重算无关注意力项。
对照参考解答
只增加两层前馈,差值为N×4×B×T×d×128=2×4×12×32×128=393216。注意力打分、四个投影和词表头均不随f改变。原总量650496因此变成1043712。

TRY IT YOURSELF

挑战:识别不成立的速度结论

有人从708032/54464约等于13断言缓存必快13倍。给出一个定量反例,并说明应补测什么。

提示 1
算术工作量以外,设两实现都有相同固定调用成本会怎样?
提示 2
构造总时间=固定项+算术项的两组数,再区分FLOPs比与实测延迟比,补同范围成对计时。
对照参考解答
设两种前向都有固定调用成本1毫秒,算术时间分别0.13和0.01毫秒,则总时间分别1.13和1.01毫秒,仅约1.12倍。需固定模型、输入、线程、计时范围,记录预热及成对多次原始时间,再检查输出等价。乘加账本是解释变量,不能替代延迟测量。

本章增量是工作负载契约:项目 README 说明固定后续输入而非采样生成,记录提示十二、后续十六、批量一及窗口六十四。后续第二章必须先证明缓存与参考函数相同,第三章再检查空间增长;只有两者通过,才允许把计时表解释为同一任务的执行差异。若改变窗口、词表或输出位置数量,应建立新的工作负载记录,不能直接把两个不一致的表格相除。

CHECK YOUR UNDERSTANDING

检验理解

先作答,再决定是否展开解析。选择题用于发现理解缺口;独立实现与迁移任务仍需另行检验。

1. 预填充最后位置主要提供什么?

查看解析提示的最后位置用于预测下一词元,即首个输出。
2. 缓存单步注意力的长度主项如何变化?

查看解析新查询仍需与所有可见K、V交互,因此按可见长度线性增长。
3. 本章FLOPs能直接推出什么?

查看解析固定开销、带宽和实际内核均未包含,不能直接推出其余结论。
4. 十个输出的首末间有多少间隔?

查看解析首输出之后还有九次到后续输出的间隔。

自行标记用于记录学习进度,不代表通过项目验收。