一条回答首字 100 毫秒返回后面却每隔半秒才出一个字另一条首字稍慢随后连续输出。只看 TTFT前者更漂亮读起来却未必更舒服。再把超时请求从报表里删掉剩下的曲线还能更好看但用户不会少遇到一次失败。10 月 16 日 vLLM 的 Kimi K3 优化文章专门讨论了正确性与跨配置性能验证。性能补丁需要回答它对哪些输入、哪些批次有效而不是只展示最快的一点。官方工程文章时间戳要从同一条请求取TTFT 是从约定起点到首个输出 Token 的时间ITL 是相邻输出 Token 的间隔。起点可以放在客户端发起、网关接收或引擎入队但报表必须写清楚不能把不同起点的数据直接比较。流式网络分块也不一定逐 Token 对齐把 HTTP chunk 当 Token会得到另一种指标。数据建议保留的字段容易造成的误读请求ID、开始、状态、结束失败没有首字于是被消失输出流首个Token、各Token时间或块边界一个chunk被当成一个Token负载输入长度、输出长度、并发、到达率不同工作量被平均到一起缓存命中长度、冷热状态热缓存数字被当作冷启动从三条教学记录算起下面全部时间都是人为构造的毫秒数用来检查统计口径不是线上性能。程序显式使用 nearest-rank 分位数避免不同库默认插值规则造成差异。import math from statistics import mean def percentile(values, p): if not values or not 0 p 1: raise ValueError(empty data or invalid percentile) xs sorted(values) if any(not math.isfinite(x) for x in xs): raise ValueError(non-finite sample) return xs[math.ceil(p * len(xs)) - 1] rows [ {start: 0, tokens: [100, 120, 180], ok: True}, {start: 0, tokens: [300, 340], ok: True}, {start: 0, tokens: [], ok: False}, ] ok [r for r in rows if r[ok]] ttft [r[tokens][0] - r[start] for r in ok] itl [b-a for r in ok for a, b in zip(r[tokens], r[tokens][1:])] assert ttft [100, 300] and itl [20, 60, 40] print(TTFT mean/p99:, mean(ttft), percentile(ttft, 0.99)) print(ITL mean/p99:, mean(itl), percentile(itl, 0.99)) print(success:, len(ok), /, len(rows))两条成功请求的平均 TTFT 是 200 毫秒nearest-rank P99 是 300 毫秒成功率是 2/3。这里只有两个成功样本所谓 P99 实际上落在最大值上不能用它描述稳定的尾延迟。样本数、窗口和置信程度应与指标一起展示。Python statistics 文档说明了均值和分位统计的不同用途也提醒 NaN 等值可能破坏排序结果。坏样本应该单独计数和调查不能静默吞掉后宣称数据更干净。官方文档把成功率放回决策比较两个方案时至少同时看满足延迟目标的成功请求数、完整输出量和总资源时间。若方案通过更早拒绝请求降低延迟报表要显示拒绝率若通过截断输出增加吞吐要显示完成条件变了。否则统计优化会伪装成系统优化。还要拆开 Prefill 主导的长输入、Decode 主导的长输出以及多轮缓存命中场景。全局一个平均数无法告诉我们哪种用户变快了。这里没有运行模型、网关或浏览器离线代码只验证三条记录的统计计算真实流式采集与集群负载仍需单独验证。