site logo

Marico's space

如何基准测试 LLM 推理性能:TTFT、ITL 和 Throughput 指标

AI技术与应用 2026-04-26 17:41:10 2

说实话,LLM 推理性能测试这事儿,我踩过不少坑。之前用的那些工具吧,要么太重(,动不动要装个 Docker 全家桶),要么统计口径跟我想要的不太一样。尤其是 ITL(Token 间隔延迟)这个指标,大多数工具都是先求每个请求的平均值,再做聚合——但我实际想看的是那些刺眼的延迟尖峰,因为那才是最影响用户体验的东西。

所以后来我自己写了个 llmperf-rs,用 Rust 实现的,主打就是一个轻量、零依赖、一个二进制跑天下。当然也是学习项目嘛,Rust 趁手写起来挺爽的。

先聊聊现有的那些工具

之前用 ray-project/llmperf(现在作者已经 archive 了),发现它对 ITL 的处理是把每个请求的 ITL 先求平均,再汇总。这个做法大多数场景够用,但我需要保留原始延迟数据,尤其是想看那些异常尖峰。

NVIDIA 的 genai-perf 其实挺全面的,但当时我的环境是 Ubuntu 22.04,不想装 Docker,跑起来麻烦。后来他们把 genai-perf 停掉了,全面转向 aiperf

vllm-bench 也挺好使,就是得先装 vllm,本地测试的时候有点不方便。

所以目标就是:一个扔进去就能跑的二进制,最好零依赖。也是边学边做的项目。

几个核心指标

完整的指标说明可以看 metrics documentation,这里我做个摘要。

Time To First Token(TTFT)——首字延迟

就是你发请求到收到第一个 token 的时间。对于对话类应用,这是用户感知到的"响应速度",直接决定体验。RAG 类应用里 prefill 阶段的大量计算也发生在这里,所以 TTFT 尤为重要。

TTFT = first_token_timestamp - request_start_timestamp

这个值越低越好。

Inter-Token Latency(ITL)——Token 间隔延迟

生成过程中,相邻两个 token 之间的间隔。延迟尖峰往往意味着问题——最常见的就是网络问题,或者 KV Cache 满了发生请求被挤出。

我在测试 vLLM 的时候发现,如果压测时逼近上下文长度上限,ITL 会出现很高的尖峰。怀疑是 vLLM 在请求超出 KV Cache 容量时会主动抢占(preemption)某些请求。

比如说,3 个请求同时来,每个带 0.8x 上下文长度加 0.2x 生成长度,但 GPU 只够放 2.8x 上下文,那必定有一个请求会被挤掉。

聚合方式:把所有响应的 ITL 值全部拼接在一起再统计,而不是先算每个请求的平均值。每个响应产生 (N-1) 个 ITL 值(N 是 token 总数)。这样能保留真实分布,包括那些刺眼的异常值。

Throughput——吞吐量

Prefill TPS:Prefill 阶段每秒处理的 token 数。

Prefill TPS = input_tokens / TTFT

但要注意,TTFT 里包含了排队等待时间,不只是实际处理时间。服务器负载高的时候,你的请求在队列里等的时间也被算进去了。所以 Prefill TPS 低,可能只是排队严重,不代表系统处理能力差。

Decode TPS:Decode 阶段每秒生成的 token 数。

Decode TPS = output_tokens / (final_time - decode_start_time)

这个才是模型真正的生成速度。

生产环境里最该关注啥

Serving 场景,重点看 TTFTITL 统计值,其次看 RPM

TTFT 决定了用户多久能看到第一个字——这是感知延迟的核心。

ITL 统计值 能暴露 Decode 阶段的问题,而吞吐量指标会掩盖这些问题。P99 和最大 ITL 值能帮我们发现 KV Cache 抢占和网络问题。对于非流式的批处理任务,ITL 的重要性就低很多了——用户又看不到 token 一个一个蹦出来。

Token 计数要准

指标准不准,首先得 token 计数准。llmperf-rs 用两种方式处理:

  1. API 返回值——大多数 OpenAI 兼容端点会在 usage 字段返回 token 数,默认优先用这个。
  2. Tokenizer——要精确的输入 token 数,可以传一个 HuggingFace tokenizer。不过要注意,Chat Template 会造成 <10 个 token 的误差。

原来的 llmperf 所有模型共用一个 tokenizer。但不同模型用的 tokenizer 是不一样的——Llama-2 词表 32000,Qwen3-4B 的词表是 151936。我实测把输入设为 8192 token 发给 Qwen 端点,但用默认的 llama tokenizer,返回值只有 7363-7376 个。

如何验证你的结果

所有 benchmark 运行结束时,应该看到 finish_reason = length(模型跑满了 max_tokens 上限)。如果看到 finish_reason = stop,说明模型提前结束了,这会影响 RPM 和端到端延迟的统计。 rejection 率高反而可能让 RPM 看起来更高、延迟更低——因为回复更短。

什么时候用 llmperf-rs

适合用:追求零依赖跑 benchmark,测试 OpenAI 兼容端点,想要低 overhead(Rust 实现,没有 Ray/ZMQ),或者就想快速摸个底。

换其他工具:需要 GPU 级细粒度指标(用 trtllm-bench 或 aiperf)、测 vLLM 特有功能、需要复杂报表 dashboard,或者做分布式压测。

为什么 Throughput 好看的时候 ITL 仍然重要

高 Throughput 但 ITL 很差 = token 一波一波地来,用户会感觉到明显的"卡顿"。ITL 尖峰(P99 > 100ms)往往意味着抢占、网络问题或其他故障。对于非用户面向的场景,比如 Agentic Coding,Throughput 可能比 ITL 细节更重要。

完整代码示例、压测结果和安装说明在 作者博客 有。

译者按:原文 https://dev.to/wheynelau/how-to-benchmark-llm-inference-performance-ttft-itl-and-throughput-metrics-416p