3. 吞吐量关键指标:吞吐量、延迟、首token延迟、TTFT、TPOT等定义与测量

在评估 vLLM 与 TensorRT-LLM 这类 LLM 推理引擎时,仅凭“快”或“慢”的主观感受远远不够。吞吐量调优的前提是建立一套精确、可复现的度量体系。本章将厘清吞吐量(Throughput)、延迟(Latency)、首 Token 延迟(TTFT)、每个输出 Token 时间(TPOT)等核心指标的定义、测量方法及它们之间的权衡关系。只有正确理解这些指标,才能避免“调优调到了错误的方向”。

3.1 核心指标定义与业务含义

LLM 推理的延迟特性与经典 Web 服务有本质区别:它不是一个单一请求的“请求-响应”时间,而是一个包含 预填充(Prefill) 与 解码(Decode) 两个阶段的流式过程。因此,我们需要用多个维度刻画性能。

指标全称/别名定义典型测量单位业务感知
吞吐量Throughput系统在单位时间内处理的请求数或生成的 Token 总数req/s 或 tokens/s系统整体处理能力,影响成本
延迟Latency单个请求从发出到完整接收所有输出的总耗时秒(s)用户等待的总时长
TTFTTime To First Token从请求发出到客户端收到第一个输出 Token 的时间毫秒(ms)“首字响应”速度,影响对话流畅感
TPOTTime Per Output Token解码阶段生成每一个 Token 的平均耗时毫秒/Token(ms/tok)输出“打字机”速度,影响阅读等待
ITLInter-Token Latency相邻两个输出 Token 之间的时间间隔(通常指流式传输间隔)毫秒(ms)流式输出的平滑度
关键认知: TTFT 主要受 预填充计算量 和 输入序列长度 影响;而 TPOT 主要受 解码阶段显存带宽 和 Batch Size 影响。两者共同决定了总延迟:总延迟 ≈ TTFT + TPOT × (输出 Token 数 - 1)。

3.2 指标间的权衡与测量陷阱

吞吐量与延迟并非总是同向变化。在连续批处理(Continuous Batching)机制下,提高吞吐量往往意味着增大 Batch Size,这会增加单个请求的排队等待时间,从而推高 TTFT 和 TPOT。因此,性能测试必须明确负载场景。

测量时需区分在线流式指标与离线批处理指标。对于在线服务,应使用并发请求模拟真实用户;对于离线任务(如批量文档总结),则更关注纯吞吐量。

常见测量误区: 仅报告“平均延迟”而不报告 P99 延迟。LLM 推理的延迟分布往往存在长尾效应,个别请求因排队或显存碎片导致延迟飙升。若只关注平均值,会掩盖系统在极端负载下的不稳定表现。

3.3 测量方法与工具链

为了准确测量上述指标,我们需要在客户端记录关键时间戳。以下是一个基于 Python 的伪代码流程,展示了如何测量 TTFT 与 TPOT:

import time
from openai import OpenAI

client = OpenAI(base_url="http://localhost:8000/v1", api_key="EMPTY")

start = time.perf_counter()
stream = client.chat.completions.create(
    model="meta-llama/Llama-3.1-8B-Instruct",
    messages=[{"role": "user", "content": "请写一篇关于AI的短文"}],
    stream=True,  # 开启流式
    max_tokens=512
)

first_token_time = None
token_times = []
for chunk in stream:
    if chunk.choices[0].delta.content:
        now = time.perf_counter()
        if first_token_time is None:
            first_token_time = now
            ttft = (now - start) * 1000  # 毫秒
        else:
            token_times.append(now)

end = time.perf_counter()
total_latency = (end - start) * 1000

# 计算 TPOT(排除首Token)
if len(token_times) > 1:
    tpot = (token_times[-1] - token_times[0]) / (len(token_times) - 1) * 1000
else:
    tpot = 0

print(f"TTFT: {ttft:.1f} ms")
print(f"TPOT: {tpot:.1f} ms/token")
print(f"Total Latency: {total_latency:.1f} ms")
print(f"Output tokens: {len(token_times) + 1}")

对于吞吐量压测,推荐使用 vllm/benchmarks/benchmark_serving.py 脚本(vLLM 自带)或 genai-perf(NVIDIA 官方工具)。这些工具支持指定并发数、请求速率、输入/输出 Token 分布,并自动汇总 TTFT、TPOT、端到端延迟及吞吐量分位数。

3.4 案例:并发数对指标的影响

我们在一台 A100-80G 上部署 vLLM,使用 Llama-3.1-8B 模型,固定输入 512 Token,输出 256 Token。通过 benchmark_serving.py 改变并发数,得到如下典型数据:

并发数吞吐量 (tokens/s)平均 TTFT (ms)平均 TPOT (ms/tok)P99 延迟 (s)
18518011.23.1
862042013.54.8
321450115019.89.6
641680235028.418.2

从数据中可以看到:当并发从 1 增至 32 时,吞吐量提升了约 17 倍,但 TTFT 增加了 6 倍以上,TPOT 也几乎翻倍。这说明吞吐量的提升是以牺牲单请求延迟为代价的。在实际部署中,必须根据业务 SLA(如 TTFT < 500ms)设定并发上限。

3.5 可视化:指标关系与测量点

下图展示了单个请求在 LLM 推理引擎中的时间线,以及各指标对应的测量区间。理解这张图有助于定位性能瓶颈究竟出现在预填充阶段还是解码阶段。

LLM 推理请求时间线与关键指标测量区间 时间 t t₀ (请求到达) 预填充 (Prefill) 处理输入 Prompt,生成首个 Token 解码 (Decode) - 自回归生成 逐个生成输出 Token t₁ (首Token) t₂ (完成) TTFT = t₁ - t₀ TPOT = (t₂ - t₁) / N 总延迟 = t₂ - t₀ 吞吐量 = 完成请求数 / 单位时间 N 为输出 Token 总数;TTFT 反映“首字响应”,TPOT 反映“后续生成速度”

图中清晰地展示了:TTFT 的测量区间是从请求到达(t₀)到首个 Token 返回(t₁),它包含了排队等待、显存分配和预填充计算的全部耗时。而 TPOT 则是解码阶段(t₁ 到 t₂)生成 N 个 Token 的平均时间。总延迟是两者之和。

3.6 误区与复盘:指标解读中的常见错误

在实际调优过程中,工程师常陷入以下误区:

  • 误区一:只优化 TTFT 而忽略 TPOT。 对于长文本生成任务(如代码生成、文章撰写),TPOT 对用户体验的影响更大。一个 TTFT 为 200ms 但 TPOT 为 50ms/token 的系统,生成 1000 个 Token 需要 50 秒;而 TTFT 为 500ms 但 TPOT 为 20ms/token 的系统,仅需 20.5 秒。后者体验反而更好。
  • 误区二:将吞吐量与延迟指标混在一起比较。 对比 vLLM 与 TensorRT-LLM 时,必须在相同并发数和相同请求分布下比较。若 vLLM 在并发 32 时吞吐量为 1500 tokens/s,而 TensorRT-LLM 在并发 8 时吞吐量为 900 tokens/s,这并不能说明 vLLM 更优,因为负载条件不同。
  • 误区三:忽略预热(Warmup)直接测量。 推理引擎在启动初期会进行 CUDA 内核编译、显存池初始化等操作,导致前几个请求延迟极高。正确做法是先发送若干预热请求,待系统稳定后再开始计时。
  • 误区四:使用固定输入长度测试。 真实场景中 Prompt 长度差异巨大。建议使用符合业务分布的输入长度(如 128、512、2048 Token 混合),否则测得的 TTFT 无法反映真实负载。
复盘建议: 建立性能基线时,至少记录以下四组数据:
① 单并发下的 TTFT 与 TPOT(反映引擎原始性能);
② 目标并发下的 P50/P95/P99 TTFT(反映排队效应);
③ 目标并发下的吞吐量(反映系统容量);
④ 显存利用率与 GPU 利用率(用于判断瓶颈在算力还是带宽)。
只有综合这四组数据,才能准确判断 vLLM 与 TensorRT-LLM 在不同场景下的优劣,并为后续的 Batch Size、KV Cache 策略调优提供依据。