Skip to content

约 6 分钟阅读

20. 性能测试与分析 ​

前面每一篇都在说“某项优化能让某个指标变好”。这一篇回答怎样证明:怎样做一次有解释力的压测,怎样找到吞吐与延迟的拐点,怎样用分析工具定位瓶颈,以及怎样防止升级后性能悄悄退化。读完你应该能设计一组对照实验,留下可复现的记录,并把一次性能变化归因到具体的机制上,而不是只得到“快了 20%”这样一个数字。

先固定条件,再改变一个因素 ​

性能测试最常见的问题不是工具不对,而是条件不一致:两次测试用的模型版本、对话模板、输出长度、并发方式不同,得出的“提升”无法解释。

一次可复现的测试,至少要记录这些信息:

yaml
model: 模型标识与版本
template: 对话模板与分词器版本
engine: 引擎名称与精确版本、镜像摘要
device: GPU 型号、数量、显存、互联方式
software: 驱动、CUDA 版本、注意力后端
precision: 权重、激活、KV 的数据类型
limits: 最大上下文、max_num_seqs、token 预算
generation: 温度、top_p、max_tokens、停止条件、是否开启思考模式
cache: 前缀缓存冷热状态
load: 输入输出长度分布、并发数或请求到达率
quality: 固定的质量样本与通过情况

然后每次只改变一个因素。比如比较两个引擎,就保持模型、精度、负载完全相同;评估 FP8 KV,就只改 --kv-cache-dtype 这一项。

同一个请求的客户端计时与服务端分段定位

先确认回答对,再测快不快 ​

压测之前,先发几个请求,确认输出正常:

bash
curl --fail http://127.0.0.1:8000/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{"model": "Qwen/Qwen3-8B",
       "messages": [{"role": "user", "content": "用三句话解释 KV cache。"}],
       "max_tokens": 256, "temperature": 0, "stream": true}'

检查回答是否完整、结束原因是正常停止还是被长度截断、思考内容和正文是否分开。Qwen3 这类模型默认会先输出一段思考过程,开不开思考模式,输出长度可能差好几倍。如果两组对比的输出长度不同,速度比较就没有意义。

用 vLLM 自带的压测工具 ​

vLLM 提供了 vllm bench serve 命令,可以对任何兼容 OpenAI 接口的服务(包括 SGLang)发起压测:

bash
vllm bench serve \
  --model Qwen/Qwen3-8B \
  --dataset-name random --random-input-len 2000 --random-output-len 200 \
  --num-prompts 200 --max-concurrency 16 \
  --percentile-metrics ttft,tpot,itl,e2el --metric-percentiles 50,95,99

它报告的几个指标和第 5 篇的定义一一对应:

  • TTFT:首 token 延迟。
  • TPOT:每个请求的 (端到端延迟 − TTFT) ÷ (输出 token 数 − 1),排除了第一个 token。
  • ITL:所有请求所有相邻两次流式返回之间的间隔,汇总成分布,能看出偶发的长停顿。
  • E2EL:端到端延迟。

还可以用 --goodput ttft:1000 tpot:50 这样的参数直接统计满足 SLO 的有效吞吐。

需要注意,random 数据集用的是随机 token,不会触发前缀缓存,也不代表真实的输出长度分布。最终评估要用接近业务的数据:真实的系统提示、真实的输入长度分布、真实的输出长度。

如果只想测单个请求的首字和流式间隔,可以用这个只依赖 Python 标准库的小脚本。它按正文的第一个片段计算 TTFT,不会把思考内容误当成正文,并单独标出错误和截断:

下载 measure_stream.py

bash
python measure_stream.py \
  --url http://127.0.0.1:8000/v1/chat/completions \
  --model Qwen/Qwen3-8B --prompt '用三句话解释 KV cache。' \
  --max-tokens 256 --requests 8 --concurrency 2

五组有解释力的实验 ​

实验固定什么改变什么观察什么
单请求基线模型、精度、长度、采样引擎或版本TTFT、TPOT、冷启动时间
冷热前缀长文档 + 不同的问题首次请求与重复前缀命中 token 数、TTFT 的变化
并发扫描输入输出长度分布并发 1、2、4、8、16……吞吐的拐点、P95/P99 延迟、抢占次数
长短混合同一组请求到达顺序、token 预算正在生成的请求的 ITL 尖刺
单项优化质量集与负载量化、投机解码等一个开关质量、显存、TTFT、TPOT、Goodput

做缓存实验时,要分别测真正的冷请求(重启服务或清空缓存后)、完全重复的请求、共享文档换问题的请求。两个引擎比较时,一个是热缓存、另一个是冷缓存,结论就是错的。

找到吞吐与延迟的拐点 ​

并发扫描是容量规划最重要的实验。把并发从 1 开始逐步加倍,每一档记录系统吞吐和延迟分位数,画成一张图:

并发扫描示意:随着并发增加,吞吐先快速上升后趋于平缓,P99 延迟在拐点之后急剧上升

典型的形状是:低并发时,吞吐随并发近似线性增长,延迟几乎不变(第 6 篇:Decode 访存受限,批变大几乎不增加步长);接近某个点后,吞吐增长变慢,延迟开始明显上升(KV 读取占比变大、开始抢占、排队);再往后,吞吐不再增长,延迟急剧恶化。

图中标出的拐点,就是在满足延迟目标的前提下这套部署的容量。错误率或尾延迟失控时就该停止加压,这个转折本身就是要找的容量信息。

两种发压方式 ​

固定并发(闭环):始终保持 N 个请求在途,完成一个再发一个。服务变慢时,发压也自然变慢。它适合观察“某个并发下的表现”,但会掩盖真实的排队问题。

固定到达率(开环):按每秒 R 个请求的节奏发送,不管之前的请求有没有完成,可以用泊松分布模拟真实用户的随机到达。它能暴露系统扛不住时队列越来越长、延迟失控的情况,更接近线上。vllm bench serve 的 --request-rate 参数就是这种方式。

稳定状态下,可以用利特尔定律做一个交叉检查:系统内平均请求数 = 平均到达率 × 平均停留时间。比如每秒到达 10 个请求、每个平均停留 3 秒,系统里平均就有 30 个请求在途。如果你的并发上限是 16,这个负载必然会排队。

定位瓶颈:从现象到机制 ​

压测告诉你“慢了”,分析告诉你“为什么慢”。先根据现象缩小范围:

现象先拆解什么下一步验证
第一次很慢,之后正常模型加载、编译、图录制、冷缓存分别记录冷启动和热请求
TTFT 高,出字正常排队、分词、Prefill、对话模板对比短输入和长输入,冷热前缀
生成中途偶尔停顿长 Prefill 插入、抢占、CPU 空隙长短混合实验,查看抢占计数
并发高时吞吐下降KV 容量、抢占重算、CPU 调度降低并发,看缓存使用率和等待队列
量化后反而变慢内核选择、反量化开销、批大小查看启动日志里实际使用的内核
多卡反而变慢卡间互联、通信占比能放进单卡时做单卡对照
结构化输出出错Schema 支持、截断、与投机解码的组合先关闭其他功能,用简单 Schema 逐项恢复

然后用工具看细节:

  • 引擎指标。 vLLM 和 SGLang 都暴露 Prometheus 格式的指标:运行中和等待中的请求数、KV 缓存使用率、前缀命中率、抢占次数、TTFT 和 TPOT 的直方图。这是线上最先看的东西。
  • PyTorch Profiler。 可以看到每一步里 CPU 和 GPU 的时间线,哪些内核耗时最长,GPU 之间有没有空隙。vLLM 支持通过环境变量开启一段时间的采样。
  • Nsight Systems。 NVIDIA 的系统级分析工具,能看到 CUDA 内核、内存拷贝、NCCL 通信和 CPU 线程在时间轴上的关系,适合定位“GPU 在等什么”。
  • Nsight Compute。 针对单个内核的深入分析,能看到它的算术强度、带宽利用率,落在 Roofline 的哪个位置。

分析时要把客户端观察到的时间和服务端日志对应起来,用请求 ID 串起来。只看一个“GPU 利用率”数字,分不清是在排队、在计算还是在传输。nvidia-smi 显示的 GPU 利用率只表示“这段时间里有内核在运行”,不表示算力被用满,Decode 时它可能显示 100%,实际算力利用率只有个位数。

防止性能悄悄退化 ​

推理引擎、驱动、模型每次升级都可能带来性能变化。把压测变成一道回归检查:

  1. 固定一套代表业务的基准负载和质量集,作为每次变更的必测项。
  2. 每次升级前后各跑一次,对比关键指标的分位数,而不只是平均值。
  3. 设定可以接受的退化范围,超出就阻止上线,再用分析工具定位原因。
  4. 保留每次的完整记录和回退版本。

最后,不能用一次测试的数字外推。“在 H100 上用 8B 模型、输入 2000、输出 200 测得 TPOT 12 毫秒”只在这组条件下成立。换了模型、硬件、长度分布或并发,都需要重新测。

可信的性能测试先固定条件、确认输出正确,每次只改一个因素;用 vllm bench serve 等工具测 TTFT、TPOT、ITL 的分位数和 Goodput,用并发扫描找到吞吐与延迟的拐点;开环发压更接近线上。定位瓶颈时,从现象缩小范围,再用引擎指标、PyTorch Profiler 和 Nsight 工具看细节。

测试证明了部署方案可行,下一步是让它长期稳定地运行。下一篇讲生产部署与运维。

别急,先让缓存热一下。