loading

Loading

探索与发现

首页 AI 开发实战

推理服务压测:流量回放的保真度账本

字数: (2651)
阅读: (2)
0
摘要:推理服务压测与 Web 接口压测不同,输出 token 数是生成出来的,没法从请求里读到。录制流量回放时会因为输入长度分布、多轮会话聚合、到达过程被固定 TPS 平滑三处失真,测出偏乐观的延迟和吞吐。本文按这三条路径拆解,给出一份压测前应检查的保真度清单。

回放不是拷贝:推理服务压测的三处失真

做过在线推理服务的压测,多半踩过同一个坑:用录制的线上流量回放,监控面板上 QPS 和 TTFT 都挺好看,上线后一遇到真实用户,P99 延迟却翻了一倍。问题往往不在压测工具本身,而在流量回放的保真度:录下来的请求和线上实际到达的请求,已经不是一个东西了。

推理服务的压测跟传统 Web 接口压测有个根本区别。Web 接口的响应时间由业务逻辑和下游依赖决定,请求体大小对延迟的影响相对线性;推理服务不一样,输出 token 数是生成出来的,没法从请求里直接读到。请求里只有一个 prompt,输出多长取决于模型和解码参数。压测时如果只回放输入,不控制输出分布,测出来的吞吐和延迟就是一个偏乐观的数字。美团 Quake 那套流量录制回放机制设计得很完整,录制入口、回放施压、Mock 配置化都有,但它解决的是请求到达侧的保真;推理服务还要多管一层生成侧的保真。

下面按三条失真路径拆:输入分布、会话状态、并发与到达过程。每一条都对应压测工具里可以落地检查的点。

输入分布失真:长度与前缀缓存

线上流量的输入长度分布通常是个长尾:大部分请求几千 token,少量请求几万 token。按请求条数均匀回放,长请求的权重会被严重低估。但推理服务的算力消耗跟输入长度不是线性关系:prefill 阶段是 O(n²) 量级的注意力计算,几条超长请求就能把 TTFT 拉高。更麻烦的是前缀缓存。线上大量请求共享系统提示词,前缀缓存命中率高,prefill 成本被摊薄。回放时如果每条请求独立起会话,缓存命中率骤降,测出来的 TTFT 和线上对不上,还不能简单归因于服务变慢。

做法上可以考虑两层。回放集不按请求条数采样,而按 token 总量采样,保证输入长度分布的分位数跟线上一致;vLLM 在线压测这类工具会把输入输出长度作为配置项,需要显式指定。另外,前缀缓存要在回放前单独校验:可以跑一条已知共享前缀的请求序列,观察压测侧的缓存命中 log,命中率跟线上差超过一截就先修回放方式,别看性能数字。

会话状态失真:多轮对话被压成单轮

录制的原始 HTTP 流量里,多轮对话是分开的请求,每一轮带着完整历史。压测引擎常见的做法是把这些请求拆开独立回放,结果就是每条请求都变成一个无状态的单轮补全,对话里重复出现的系统提示词被重复计费,prefix cache 收益丢失,KV cache 的回收节奏也变了:线上因为共享前缀、复用会话,KV 块复用率高,回放时却是大量一次性块,显存压力偏高、淘汰更频繁。这两者叠加,容易把明明能跑的系统压出 OOM。

这件事量级有多大,取决于业务里多轮对话的比例。客服、Agent 类的推理服务,多轮占比能到七八成,失真就不可忽略。回放工具需要按会话 ID 聚合请求,按原始间隔串联重放。代价是回放时长被拉长,得在并发上做补偿,见下一节。

自然到达与倍速回放:泊松还是固定 QPS

流量录制回放工具通常支持固定 TPS,也支持倍速回放,这一点顺丰同城压测平台和 GoReplay 的改造里都有对应功能。但固定 TPS 回放会撒谎:真实流量的到达是带批次波动的,推理请求尤其明显,用户一次性提交、前端批量上传、定时任务整批打进来,这些会在秒级形成突发。固定速率恰好把这个突发抹平了,测出来的队列长度、批内超时几乎永远不会出现。

针对推理服务,可以在回放里引入到达过程控制:按录制到的每秒到达数按比例缩放,记录原始的突发形状,而不是一个平均 QPS。需要备注的是,如果要按方向压测,inference-perf 确实提供了几种输入输出长度分布(固定、高斯、min-max)和突发负载模式,值得配几组对照,但具体实施前要读一遍行为是否符合业务形态——这些维度是推理服务特有的,不能假定通用压测平台的参数直接可套。

输出长度算不算盲区

在输入长度之外,输出是压测引擎更难管住的部分。同一条 prompt 反复回放,最优的服务端会生成相同的合理响应,但解码热路径带来的吞吐影响远不如实际业务差异大。真实场景里同一个 prompt,用户在不同会话中要求的输出长度不一样:摘要、改写要求长度固定,对话、Agent 链负责完成则随时停。这个多维指标测量工具通常报告 TTFT、ITL、TPOT、E2EL,用来把每次回放快照单独打标,对比一组倍速下的解码延迟变化,如果固定输出量,回放就成了分组合并的那波序列,反而掩盖了热段。

折中的做法是控制 max_tokens 和线上分布对齐(比如按 p50/p90/p99 分档回放一定的比例),并且记录一批输出实际长度,在压测报告里区分下采样截断会影响生成的端到端延迟,但当前已经接受对生成侧回放做定量校准的工具不多,这还是个手工活。

一张压测前应检查的回放保真度清单

检查这几个点,会让压测结果对自己诚实很多:

  1. 回放集按 token 采样的分位数与线上对齐:输入长度 p50/p90/p99 是否大致吻合。
  2. 多轮会话是否按会话聚合成组回放、复用历史是否一致,前缀缓存命中率与线上同比。
  3. 到达 QPS 是否保留突发形状,倍速回放标注了实际平均 QPS 和峰值。
  4. 输出长度分布是否收到回放集里,max_tokens 设置是否跟线上同步;批内跨请求共用短前缀的语义参数没有被当成纯条数来估算,保持计数。
  5. 影子库/影子缓存是否杜绝旧前缀复用对压测场景的污染:录制的请求头部与挡板是否可用,避免真实下游被命中——这点美团 Quake 的「出口 Mock配置化」值得借鉴;检索资料也提醒,按异常规则保真而不干净的请求体来做。
  6. 保真度指标缺少显式报告,不确定就明说:比如缺少“回放命中与实际发生样本匹配”的定量值,如果没法算出来,就在记录文档里写下来,不把不确定标成确认过。这些检查比报告几个漂亮数字便宜,能避免很多事后归因的时间。
  7. 压测后对比报告时注明回放集的失真方向,哪些会让结果乐观、哪些让结果悲观,以便后续做偏差修正。

一些底层差异的提醒

压测本质上是在给系统上建模的负载,回放集是一个方便但不完整、不能要求百分之百保真的替身。上线估值时,可以把推理压测当作一个趋势指标(压测数字在迭代间可比),而不是线上必然对应的确定数。尤其是 GPU 这类受温度、ECC、卡间通信干扰的硬件,不同回放策略下的波动常常比优化本身的影响还大。这时候,再把固定 TPS 下的结果当成签名,反而是在浪费时间。同一套流量录制和回放链路一旦长期复用,把回放集版本号落进实验结果里,这件事本身就不小。

转载请注明出处: 皮哈皮阿

本文的链接地址: https://blog.phpat.com/ai-dev/676.html

手机扫码阅读

微信或手机浏览器扫一扫,随时随地随心阅读与分享

您可能对以下文章感兴趣
1 / 1
预览图