🚦 LLM 推理性能测试实践
1. 与传统接口压测的差异
| 维度 | 传统 API | LLM 推理服务 | 测试对策 |
|---|---|---|---|
| 响应 | 定长 JSON | 变长流式 token | 指标拆 TTFT/TPOT;输出长度作为一等公民记录 |
| 瓶颈资源 | CPU/连接数/DB | GPU 显存(KV Cache)+ 显存带宽 | 监控显存/KV 占用/抢占次数,不只看 CPU |
| 排队行为 | 线程池,先到先服务 | 调度器 preemption:显存不足时抢占重算 | 把"抢占率"纳入错误/劣化指标 |
| 结果正确性 | 确定性校验 | 采样不确定性 | 性能测试用 temperature=0 或固定 seed,正确性另走评测集 |
| 基线 | 环境+版本 | 模型+量化+引擎+max-model-len+硬件(五元组) | 基线键缺一即重测,报告头部强制列出 |
2. 测试设计(五步)
- 基线:并发=1 × 50 请求(1K/500 长度),P50/P95 的 TTFT/TPOT 入档。
- 梯度:并发 1/2/4/8/16/32/64 各 ≥200 请求,产出「并发 × 指标」矩阵;标注两个拐点(TPOT 陡升=decode 饱和,TTFT 陡升=排队)。
- 场景:按流量配比混合(短问答/长输入/长输出)+ 长上下文专项(32K/128K/153K 输入 TTFT 曲线 + 混跑挤占实验)。
- 稳定性:设计点 80% 持续 ≥30 分钟(容量验收);消费级卡加 2 小时高温满载专项(ECC/降频/输出异常率)。
- 极限与恢复:压至 OOM 或错误率 >5% → 撤载 → 记录自恢复时间与首请求劣化窗口。
3. 报告模板(最小集)
- 头部:五元组基线键 + 压测工具与版本 + 机器配置 + 日期
- 矩阵表:并发 × {请求数, TTFT P50/P95/P99, TPOT P50/P95, 吞吐 tokens/s, 错误率, 峰值显存, KV 命中率, 抢占次数}
- 拐点标注:decode 饱和点、排队起点、OOM 点
- 结论:SLO 约束下的 Goodput(并发数 + tokens/s)、容量建议(实例数 × 冗余)、风险(长上下文挤占/稳定性边界)
- 附件:原始逐请求数据(csv)、监控截图(显存/SM 利用率/队列长度时间轴)
📌 与站内的衔接:方法论通用部分(梯度设计、拐点分析、报告规范)见 性能测试体系·方法论;本文聚焦 LLM 推理特有的指标与资源模型。工具侧建议:客户端用可自定义 SSE 埋点的压测脚本(k6/httpx 均可),服务端接 vLLM 内置 /metrics + Grafana。
4. 坑点清单(血泪版)
- 首波请求不预热 → TTFT 虚高 3-10 倍(JIT、CUDA 图捕获、显存首次分配)。
- prompt 池太小被 Prefix Cache 全命中 → TTFT 假性优秀;报告必须附命中率,命中率 >80% 的 TTFT 数据要单独标注。
- 用"平均响应时间"写结论 → 被长输出请求带偏;一切 P95/P99 起步。
- 跨模型横评只比响应时间不比输出长度与质量 → 结论无效。
- max-model-len 设满 176K 测短场景 → 并发容量被 KV 预留吃掉,测出来的"差"是配置问题不是模型问题。
- 多实例压测忘记 LB 层自身的连接与缓冲限制(LB 先于 GPU 成为瓶颈)。
- 稳定性测试里显存缓慢上涨不定位(Python 对象泄漏 vs KV 块释放延迟,处置方式完全不同)。