AI AI infra · 推理侧
第 1 章
AI infra · 推理侧

第 11 章 评测与成本:把时间换成钱

前十章讲的都是"怎么更快"。但一个服务是否成立,取决于另外两个问题:它能接多少请求,每个 token 花多少钱。这两个问题都不能靠感觉回答,只能靠测量。

这一章讲三件事:测什么、怎么测、怎么把测量结果换成成本。先说清楚两个指标和它们背后的分位数,再说压测的方法,最后算钱。

两个指标:首字与出字

用户感知的延迟可以拆成两段,对应两个指标。

首字延迟(TTFT)是从请求发出到看见第一个字的时间。它由预填充决定,也包含排队时间。这个指标决定了用户觉得"这个产品反不反应"。

出字速度(TPS)是第一个字之后,每秒产生多少个 token。它由逐字生成决定,瓶颈在带宽。这个指标决定了用户觉得"读起来顺不顺"。

TPS 有三个容易混淆的版本。单用户视角的每秒 token 数,是延迟指标;整个服务每秒产出的 token 总数,是吞吐指标;相邻两个 token 之间的间隔叫 ITL,10 毫秒对应每秒 100 个。三个数说的不是一回事,讨论性能时必须说明是哪一个。

还有一个常被忽略的事实:这两个指标由不同阶段决定,所以优化手段不通用。量化、投机解码改善 TPS;切块预填充、前缀缓存改善 TTFT;批处理同时影响两者,但方向相反。

平均值会骗人

延迟不是一个对称分布。大多数请求集中在某个值附近,少数请求会拖得很长:缓存没命中、赶上扩容、被一条长请求堵住。平均值会被这些长尾拉高,也会掩盖长尾本身。

所以看分位数。

分位数含义
P50中位数,一半请求比它慢
P90百分之十的请求比它慢
P95百分之五的请求比它慢
P99百分之一的请求比它慢

P50 好看不代表体验好。如果每一百次交互就有一次卡好几秒,用户会记住那一次。Inference Engineering 的说法是,好的性能工作要把 P90 和 P99 一起压下去,而不是只优化平均。

分位数的另一个作用是定位问题。P50 正常、P99 很差,说明大部分请求没问题,少数请求撞上了某种资源竞争——缓存淘汰、队列抖动、跨机通信重试。P50 本身就差,说明是系统性的容量不足。

端到端不等于推理

推理时间只是链路的一段。请求还要经过网关、鉴权、预处理、后处理、网络传输。

如果推理很快而端到端很慢,问题不在模型,在基础设施。这时候继续调引擎参数是白费力气,该看的是网关延迟、序列化开销、跨区域传输。

Inference Engineering 把这个区分说得很直接:推理时间告诉你模型优化有没有效,端到端指标告诉用户实际感受。两个都要测,而且要分开测。

怎么压测

压测最容易犯的错是用不真实的负载。

用固定长度的输入、均匀的到达间隔去压,得到的数字很好看,但和真实流量没有关系。真实流量是抖动的:请求随机到达,长度参差不齐,偶尔来一批长文档。这种抖动会让队列忽长忽短,延迟分布和均匀负载完全不同。

所以压测要满足四条。第一,ISL 和 OSL 按真实分布采样,不是取平均值。第二,到达时间加抖动,模拟突发。第三,先预热,让引擎把缓存、编译、连接都准备好,再开始计时。第四,测多组配置,批大小、并行度、量化各变一次,看趋势而不是看单点。

工具方面,NVIDIA 的 genai-perf、vLLM 和 SGLang 自带的 benchmark 脚本都能做,性能剖析用 PyTorch Profiler 或者 Nsight。工具不是难点,难点是让负载像真的。

并发该按多少算

有一个简单的公式可以估并发需求,叫利特尔法则:系统中的平均请求数,等于到达率乘以平均停留时间。

举例。假设每秒来 10 条请求,每条从进入到结束平均 3.9 秒(第 2 章算过的那条 8K 输入、512 输出的请求)。那么在系统里的平均请求数就是 39 条。

这 39 条就是你需要的同时处理能力。如果一张卡只能并 12 条,就需要四张卡。如果四张卡还不够,要么加卡,要么缩短停留时间,要么在入口限流。

这个公式的价值在于它把"要几台机器"变成了一个除法。很多容量规划靠拍脑袋,其实只需要三个数:到达率、单条耗时、单机并发。

它也有前提:系统稳定,请求不会无限堆积。过载时队列会一直涨,利特尔法则算出来的并发数没有意义,这时候该做的是限流。

成本怎么算

算成本的基本单位是 GPU 小时,不是 token。

一张卡每小时多少钱,乘以卡数,乘以运行小时数,就是总成本。把它除以这段时间产出的 token 数,得到每百万 token 的成本。

关键是分母。分母取决于吞吐,吞吐取决于批大小、量化、缓存命中率。同样一张卡,优化前后每百万 token 的成本可以差三倍。

举个例子,数字只为说明方法。假设一张 A100 每小时两美元,跑 7B 模型,批大小 32,每步 6.9 毫秒。一步产出 32 个 token,一秒约 4600 个,一小时约 1670 万个。成本是两美元除以 16.7,约每百万 token 一毛二。

如果实际利用率只有一半,成本翻倍到两毛四。如果上了 FP8 和前缀缓存,吞吐翻倍,成本回到一毛出头。如果并发上不去、卡空转,成本可能到一块钱以上。

所以"每百万 token 多少钱"这个报价,必须附三个条件:什么卡、什么模型、什么负载。缺一个就没法比较。公开 API 的价格看起来便宜,往往是因为批量大、利用率高、模型小;自建看起来贵,往往是因为利用率低。

扩容与冷启动

流量不是恒定的。白天高、夜里低,活动期间会突然涨几倍。容量按峰值准备,大部分时间在浪费;按均值准备,峰值会崩。

自动扩缩是折中:按当前负载动态调整副本数。触发条件通常不是 CPU 使用率,而是队列长度或者首字延迟——这两个才直接反映用户感受。

难点在冷启动。一个新副本从启动到能服务,要加载权重、初始化引擎、预热缓存,几分钟很正常。等队列变长再扩,用户已经等了几分钟。所以扩缩策略要提前:按预测扩,或者保留一定的缓冲容量。

多区域部署是另一层。跨区域能降低网络延迟、提高可用性,但模型权重要在每个区域各放一份,成本翻倍。只有用户分布广、延迟敏感的场景才值得。

一份压测报告该有什么

压测结果经常不可比,因为报告里缺信息。一份能用的报告至少要写清五样。

第一是硬件和模型:什么卡、几张、什么精度、什么引擎和版本。同一张卡,引擎版本不同,数字能差一成。

第二是负载分布:ISL 和 OSL 的中位数、P90、最大值,以及到达率曲线。只写"平均 8K"没有意义,因为分布的形状决定了批的构成。

第三是配置:批大小上限、并行度、量化方案、缓存策略。缺一个,别人就复现不了。

第四是分位数:TTFT 和 TPOT 各自的 P50、P90、P99,而不是平均值。平均值会把长尾藏起来。

第五是持续时间:跑了多久,有没有预热,有没有出现队列持续增长。跑三十秒的峰值数字和跑一小时的稳态数字,不是一回事。

有这五样,两组数字才能比较。缺一样,就只能当参考,不能当结论。

成本的边界

三件事不能靠优化解决。

第一,模型本身的成本。2350 亿参数的模型,权重 440 GiB,无论怎么优化,都要六张卡起步。这部分是固定的。

第二,需求的不确定性。输出长度不可预测,成本就不可预测。设最大长度和超时是唯一的控制手段。

第三,峰谷差。按峰值准备容量,利用率必然低;按均值准备,峰值必然排队。这个矛盾只能靠定价或者排队策略缓解,不能靠技术消除。

能做的三件事是:把吞吐压到硬件上限附近,把缓存命中率提上去,把扩容做在队列变长之前。这三件事做完,成本通常能降一半以上,剩下的就是模型和需求本身决定的。

下一章把前面十一章全部用一遍,走一个完整的案例:从需求到选型,从上线到故障排查。