当前位置:首页 > 未命名 > 正文内容

压测推理服务:只报 tokens/s 是不够的

xiaorui6小时前未命名1

"我们的推理服务能跑到 3000 tokens/s"——这句话基本没有信息量,因为它没说是用什么 prompt 长度、什么并发、什么输出长度跑出来的。这篇讲怎么做一个能拿来做容量规划的压测。

验证截至 2026-10-21。方法论综合自多家公开工程实践文章(彼此结论一致),指标定义与 NVIDIA 的推理性能基准口径对齐。

环境与版本

  • 适用对象:自建推理服务(vLLM / SGLang / TensorRT-LLM 等)或托管推理端点,上线前的容量评估
  • 压测工具不限,但计时必须在客户端做,原因见下文
  • 需要的前置数据:生产日志里的 prompt 长度分布、输出长度分布、典型与峰值并发

为什么传统 RPS 压测在这里是错的

REST 接口的服务时间基本恒定,所以按每秒请求数容量规划是成立的。LLM 端点在四个地方打破了这个模型:

1. 输出长度是随机变量。 端到端延迟 ≈ TTFT + 输出 token 数 × 单 token 耗时,同一个 prompt 每次跑出来都不一样。拿一个固定 prompt 采样一次,说明不了 p95。

2. 流式会长时间占着连接。 一次生成占用一个在途槽位 4–20 秒,这里的"并发"指的是并发生成数。按利特尔法则:在途数 = 到达率 × 平均服务时间。

3. 排队可能发生在别人的机器上。 用托管端点时,你自己的进程可能 CPU 才 5%,而 p95 TTFT 已经翻了三倍——队列在服务商那边。此时本地资源指标毫无参考价值。

4. 压测本身有账单。 成本随生成 token 数线性增长,输出长度失控会让一次测试贵十倍;托管服务的限流通常是按 key 的请求数 + token 数双限,拿服务真实用户的 key 去压测会直接把线上打限流。

结论:从单 prompt、非流式、低并发的测试外推出来的容量,偏差方向是乐观的——也就是危险的方向。

必测的四个指标,一个都不能少

推理是两阶段过程:prefill 读完全部 prompt 产出第一个 token,decode 逐 token 生成剩余部分。两个阶段受限于不同资源、失败方式也不同,所以必须分开测。

指标定义用户感知
TTFT发出请求到收到第一个 token"有没有反应"
TPOT / ITLdecode 阶段相邻 token 的间隔"阅读速度顺不顺"
吞吐全部并发请求合计的 tokens/s容量,用于规划
尾延迟TTFT 与端到端的 p95 / p99决定会不会超时

两个纪律:

  • 报百分位,不报均值。 延迟分布是重尾的,少数卡在队列里的请求会把均值拖得到处跑。
  • 不要把 TTFT 并进端到端里看。 TTFT 主要由排队 + prefill 决定,剩下的由 decode 决定。把系统提示词砍一半会改善 TTFT 但完全不动 TPOT;服务端调大批次则正好相反。

另外有个比吞吐更有用的量:goodput——同时满足 TTFT 和端到端 SLO 的请求数。吞吐很高但 TTFT 全部超时的配置是不能上线的,goodput 才是可用容量。

并发要扫,不要选一个点

用同一份请求集,在并发 1、4、16、64 逐级往上跑,直到服务饱和或开始报错。每一级记录全部四个指标。你要找两个点:

  • 吞吐曲线的拐点:再往上加并发,吞吐不再增长。这是你的有效容量。
  • 延迟预算被突破的位置:TTFT 或 TPOT 超过产品能容忍的值。

在多数栈上,第二个点出现在第一个点之前。拐点前一级的并发才是你真正能跑的负载,通常远低于理论最大并发。这两个点之间的距离就是你的余量——距离宽的栈降级平缓,距离窄的栈从"正常"到"不可用"可能只差一点点负载。

让测试长得像真实流量

最常见也最致命的错误,是用错误形状的请求在做测试。100 token prompt、100 token 输出的吞吐数字,对"3 万 token prompt、300 token 输出"的负载毫无参考价值。

开工前从生产日志里拉出这五项:prompt 长度分布(不只是均值)、输出长度分布、prompt 与输出的比例、典型与峰值并发、是否流式以及是否使用工具调用或结构化输出。然后按这个分布造请求集,能用真实 prompt 就别用合成的。

几个具体坑:

  • 不要重复发同一个 prompt 几千次——会命中 prefix caching,测出一个"又快又便宜但真实用户根本享受不到"的系统。每个长度桶至少准备几十条不同 prompt,或加随机 nonce。
  • max_tokens 按生产的上限设。 不设上限会把 60 秒的测试变成几千 token 的测试,预算直接爆掉。
  • 想清楚你是闭环还是开环。 固定 worker 数的闭环压测会自我限流:延迟一上升,每个 worker 完成得少,实际施加的负载悄悄下降,于是你低估了排队。要找到真正的拐点,需要固定到达率的开环压测。

跑多久算够

参考口径:每个配置至少 1000 次请求或 5–10 分钟,且必须先做 5–10 次预热再开始计时。生产签收再加一轮一小时左右的 soak test,用来抓短测跑不出来的东西——显存缓慢增长、KV cache 碎片、以及降频。

冷启动延迟(模型加载 + CUDA graph 捕获 + 缓存填充)要单独记录,大模型可以长达几十秒。压测报告里建议报热态延迟,但容量规划和自动扩缩容必须单独把冷启动时间算进去。

依赖什么功能,就测什么功能

兼容性是格式声明,行为得验证。路由生产流量之前,至少过一遍:

  • 流式:确认 token 是增量到达的,长生成不会中途断连
  • 工具调用:确认返回结构是你的解析器能吃的,包括并行调用、空参数对象这类边界
  • 结构化输出:用你真实的 schema 测,尤其是大的 schema
  • 长上下文:发送长度分布最右端的 prompt,有些栈能正确处理但 TTFT 比均值暗示的差得多
  • 采样参数:确认 stop、temperature 等真的生效,而不是被静默忽略
  • 错误语义:故意打过载,看返回的是干净的 429 带重试提示、超时、还是半个响应——你的重试逻辑依赖这个

边界与坑

  • 用独立的 API key 做压测,别污染线上配额
  • 客户端打点,别看服务端资源利用率当饱和信号
  • 引用他人 benchmark 数字时,先找它的 prompt 长度、输出长度和并发;口径不同的数字不要放进同一张表比较
  • 对比两个候选配置时,只变硬件或只变一个变量,其余全部固定;没测到的维度写"未测",不要推断

常见问题

Q:只报一个 tokens/s 够吗?

不够。没有 prompt 长度、输出长度、并发这三个前提,这个数字无法用于规划。

Q:为什么我的压测结果比厂商宣传的高?

多半是命中了 prefix caching,或者请求形状比真实负载短得多。

Q:闭环和开环要选哪个?

闭环上手快,但找拐点必须用开环——闭环会在延迟上升时自动降低施加的负载。

Q:并发加到多少停?

加到 p95/p99 TTFT 或 TPOT 突破你的 SLO 为止,那个点就是上限,不是加到报错为止。

Q:需要跑多久?

单配置 1000 次请求或 5–10 分钟起步,预热之后才开始计时;上线前加一轮小时级 soak。

参考与延伸

  • Freestyle《How to benchmark LLM inference before you commit to capacity》——四指标定义、并发扫描与依赖功能测试清单
  • QoraAPI《Load Testing LLM Apps: Throughput, TTFT, and Concurrency》——闭环/开环差异、goodput、成本随 token 线性增长
  • HinterBuild《Benchmark LLM Serving: TTFT, TPOT, and Throughput Guide》——样本量与 soak test 的时长口径
  • Sekin《How to Size GPU, CPU, Memory, and Storage for AI Inference Workloads》——对比测试需固定的变量清单
  • 指标定义对齐 NVIDIA 推理性能基准的 TTFT / TPOT / ITL / throughput 口径