LLM inference latency / throughput metrics overvie

👉
vLLM server提供`/metrics`端口供查阅相关的metrics: https://docs.vllm.ai/en/latest/design/metrics.html#objectives

Overview

在LLM Inference中,我们经常能看到不同的服务厂商或者推理框架汇报自己的性能,其中有很多指标(metrics),对此我们先弄清楚他们是什么,以及如何计算/衡量的
常见的metrics有TTFT, TPOT, ITL, E2EL, Throughput, RPS (QPS), TPS, Goodput等,参考这个介绍:https://bentoml.com/llm/inference-optimization/llm-inference-metrics
指标
中文
定义(要点)
常见公式 / 计算
反映什么
典型场景
注意/陷阱
TTFT (Time to First Token)
首字出字时延
发送请求到生成首个token的时间
交互“起步”速度与响应流畅感
对话/补全等需实时反馈的应用
只快TTFT但后续很慢,体验仍差。(bentoml.com)
E2EL (End-to-End Latency / Total Latency)
端到端总时延
从发出请求到接收最后一个token结束
用户完整等待时间
长回复、报表生成等
E2EL高可能由推理或网络/排队等多因素造成。(bentoml.com)
TPOT (Time per Output Token)
单字平均生成时延
首字之后,相邻输出token之间的平均时间
TPOT = (E2EL - TTFT) / (TotalOutputTokens - 1)
稳态生成速度;越低越流畅
流式界面(如聊天窗口)
不同框架/论文定义细节可能不同;阅读基准需看准口径。(bentoml.com)
ITL (Inter-Token Latency)
相邻token间隔
两个连续输出token之间的实际时间间隔
单请求下 平均ITL = TPOT;多请求下为token加权平均
更细粒度观测抖动与稳态
排查“卡顿”、尾延迟
多请求平均:TPOT通常按请求加权;ITL按token加权,两者可能不同。(bentoml.com)
RPS (Requests per Second)
每秒请求数,也会叫QPS
单位时间内完成的请求数
RPS = CompletedRequests / (T1 - T2)
并发承载能力
网关/服务容量评估
不了解请求难度差异会误判(短prompt vs. 长文档)。(bentoml.com)
TPS (Tokens per Second)
每秒token数
单位时间处理/生成的token数(分输入TPS输出TPS
更细的吞吐画像(prefill vs. decode)
长输入总结 vs. 长输出聊天
必须明确是在量输入、输出还是合并口径。(bentoml.com)
Throughput
吞吐量
一段时间内系统完成的工作量总称(RPS/TPS等体现)
系统总体产能
批量处理、并发压测
追求吞吐可能牺牲单请求时延(批量、共享算力)。(bentoml.com)
Goodput
“有效”吞吐
满足SLO前提下的完成速率(如达标TTFT/E2EL的RPS)
“Goodput = 在SLO内完成的请求/秒”
真实可用体验与质量
生产SLA/SLO考核
纯吞吐高≠好体验;需结合SLO过滤无效请求。(bentoml.com)
指标
中文
定义/口径
适用解读
典型用法/示例
SLO (Service-Level Objective)
服务级目标
针对某个指标设定的达标阈值与覆盖比例(如“95% 的对话 TTFT < 200ms”)。常用于和 Goodput 结合,只统计满足 SLO 的吞吐。
让“可用的真实吞吐”与业务体验挂钩,而不是只看裸吞吐。
“P95 TTFT ≤ 200ms” 或 “P99 E2EL ≤ 2s”。 (bentoml.com)
Mean
平均数
所有值求和 / 个数,容易受极端值影响。
适合监控长期趋势,但不代表多数用户的真实体验。
“mean TTFT” 用于观察整体波动;遇到长尾时可能被抬高。 (bentoml.com)
Median
中位数(P50)
排序后正中的那一个值,比均值更抗离群,代表“典型用户”的体验。
看大多数用户的感受,比 mean 更稳定。
“median TPOT = 40ms” 表示稳态流畅;若 median TTFT = 30s,则实时性不可用。 (bentoml.com)
P99 (99th Percentile)
第99百分位
99% 的请求不超过该值,体现尾延迟与一致性。
生产体验与 SLO常看 P99;长尾差会“压坏”口碑。
“P99 E2EL = 8s”:最慢 1% 仍需 8s,需优化尾部。 (bentoml.com)
👉
一个用户发出的prompt,大致需要经历如下过程才能返回给用户:
router_schedule + proxy_time + tokenize + schedule + prepare + model-inference + sampling + de-tokenize + proxy_time
  • TTFT / E2EL
  • TPOT / ITL (多请求)
对于单个请求,每次流式返回是一串token, 所以我们可以认为ITL是inference server每个step之间的间隔,通过测量得出,而mean TPOT则是每个token所花费的时间,天生就是一个均值属性,属于测量之后计算得出。
  • Goodput
  • Balance latency / throughput
 

VLLM的实现

我们以`vllm bench serve` 为例:
bench server发送数据记录时间和返回的代码在`vllm/vllm/benchmarks/lib/endpoint_request_func.py`, 最后计算metrics的代码在`vllm/vllm/benchmarks/serve.py`
  • 收集:
notion image
  • 计算
notion image
notion image
 

TPOT和ITL的关系

  • 如果是是普通的Auto-Regression (AR) decode, 那么每次vllm llm serve每次流式返回一个token,假设总生成长度是n, 那么itl_list length=n-1, mean tpot = (e2e_latency - ttft) / (n-1) = sum(itl_list) / (n-1) = sum(itl_list) / len(itl_list) = mean itl
  • 如果是在普通的AR上加了speculative decoding,也就是每个vllm llm serve存在traget-model, draft-model接力推理的情况下,每次流式返回可能不止一个token, 假设总生成长度为n, itl_list length = m, mean tpot = (e2e_latency - ttft) / (n-1) = sum(itl_list) / (n-1), mean itl = sum(itl_list) / len(itl_list) = sum(itl_list) / m, 如果speculative decoding 指定的draft tokens长度为k,那么每个decode step生成的next_tokens_num 满足 1 ≤ next_tokens_num ≤ k+1 (额外的1属于bonus token),因此m ≤ n,那么会出现mean tpot ≤ mean itl的情况,一般来说,接受率越高,m越小,mean tpot和mean itl之间的差距就越大
  • tpot对于单request来说只有mean tpot,median / p99 tpot是针对多requests统计下的 tpot来计算的,属于request粒度,而itl属于llm server inference step粒度,所以median / p99的指标数字一般是不会相同的,itl更能反映流式生成的波动,直观上就是展示用户看到的大模型吐字的“顿挫”,“卡顿”感。
上面的第二条分析,我们得出在speculative decoding的情况下会发生mean tpot ≤ mean itl,但是它们两者之间有没有更加具体的数学关系?因为itl的mean值随着draft_model的接受率而变化,接受越多,m越小,接受越少m越大,两者也就越接近:
mean_itl / mean_tpot = sum(itl_list) / m * (n-1) / sum(itl_list) = (n-1) / m
speculative decoding有个接受率的概念,计算方式在`vllm/vllm/v1/spec_decode/metrics.py`文件里:
notion image
notion image
draft model acceptance_rate很好理解,就是target_model 总接受token数量除以总draft model 总生成tokens数量,全部接受就是1,全部拒接就是0
mean_acceptance_length比较重要,实际服务中更加关注这个指标,代码中的计算公式为:
mean_acceptance_length = 1 + (num_accepted_tokens / num_drafts)
num_drafts表示每个request跑了几次verify, 应该和draft-tokens数量设置无关(假设你没走tree-attention),所以单个requests上, num_draft_tokens = num_drafts * draft_tokens
(num_accepted_tokens / num_drafts)表示平均上,每次verify,target_model接受了多大的长度,长度应该是0~draft_tokens之间,+1代表,如果全部拒绝,verify自身会得到正确的next-token,如果全部接受会部分接受,verify会根据最后一个被接受的draft-tokens代表的logits,得到一个bonus token,所以+1来自decoder-only llm自回归特性使然,总会有一个额外的token给你兜底

现在我们回过头来看这个公式:
mean_itl / mean_tpot = sum(itl_list) / m * (n-1) / sum(itl_list) = (n-1) / m
对于一个request,在sepc_decode下,每个inference step,会生成mean_acceptance_length, 跑了m次,最终生成了n-1个next-token,所以(n-1) / m = mean_acceptance_length. 近似的,在多个requests下,我们也可以认为上述公式成立,这样我们可以得到:
mean_tpot = mean_itl / mean_acceptance_length

workload inference metric理论估计example

📌
AI Infra serve的总体目标:在满足SLO的前提下,尽量提高有效吞吐
以vllm llm serve为例,其控制requests处理并行/并发度行为,有三个重要的参数可以调节:
  • model server层面:`max_num_seqs`和`max_num_batched_tokens`
  • router层面: `max_concurrency`
router层面设置的`qps`参数不能控制并发度,它只是模拟设置的qps来采样request发送到server的间隔。并发度是针对服务层面能在单位时间/空间内处理多少个requests
  • `max_num_seqs`代表vllm engine_core scheduler一个step最多能处理多少个requests, `max_num_batched_tokens`代表vllm engine_coe scheduler一个step最多能处理多少个token, 因为vllm采取continuous batching的方式,所以requests被选中的tokens是拼在一起拉成一维处理。上述两个参数谁先达到阈值谁先生效。
  • `max_concurrency`表示router最多只能送给model server多少个requests,而不是来了就无脑送给model去管理,这个参数的目的是为了在模型上线后,调整latency-throughput之间的平衡
notion image
  • `max_num_seqs`, `max_num_batched_tokens`和`max_concurrency`三者存在短板效应,谁先达到阈值就会先限制并发,如果`max_concurrency`设大了,而`max_num_seqs`和`max_num_batched_tokens`比较小,throughput也上不去,如果`max_concurrency`设小了,而`max_num_seqs`和`max_num_batched_tokens`比较充足,虽然latency能达标,但阻塞了后续的request,会造成某些request排队等待时间过长,导致TTFT过大。
  • 既然这些参数配置比较复杂,我们如何确定llm serve期间的batch_size?比如说我们有mean TTFT和mean TPOT,他应该也有平均的bs,这样方便我们估计吞吐。一般来说,我们可以根据SLO的输入输出规模合理设置`max_num_batched_tokens`,这样我们就可以关注`max_num_seqs`,让它的阈值先到,比如可以设置阈值刚好满足SLO,然后我们设置`max_concurrency`,让其最大不能超过所有model server的总`max_num_seqs`,方便我们调节(user发request给router endponit, router下维护了多个model server endpoint, 然后根据算法选择发给哪个model server实例)。这样的话,`max_concurrency`的request都能被同时处理,我们就可以用它来代表bs,然后根据一段时间统计的mean TTFT / TPOT来计算吞吐。
接下来,我们以deepseek-r1为例(它的部署代表了现在比较前沿的水平,包含了FP8 quant + xPyD + tbo + tp + dp + ep + deepep + eplb + mtp + … etc.),来分析一下部署的metrics该怎么估计
  1. xPyD部署中的x, y如何确定?
  • 首先,1P1D中,P instance和D instance可能使用的node数量有所区别,因为他们的并行策略可能所有不同,尤其是大型MOE ep并行下,比如P可能使用2个节点,D可能使用4个节点
  • 我们首先要根据SLO标准,不断try p / d部署settings,尽可能在比较少的机器上充分利用内存,算力,带宽,满足各自的prefill / decode SLO标准
  • 然后我们开始配比x, y,因为两边能处理的max-num-seqs不一样,且P计算密集型,D是访存密集型,一般来说D能处理比P更多的requests,但是D需要等P处理完发过来,所以可能需要更多的P防止D的性能退化
    1. 设置好了xPyD,max-concurrency怎么计算?
    • 其实本质上就是上面的p_loop * b_p * x = (lo * t2 * bp * x) / t1
    • 此时的max_concurrency就是server处理的并发度,类似bs的概念,即lo*t2这段时间内,处理了max-concurency个request,且mean_ttft = t1, mean_tpot=t2,满足SLO
    1. 设置好了xPyD,max-concurrency,单机8卡有效QPM怎么算?
    为了满足SLO,提高吞吐QPM,我们可以:
    • 加机器,增大max_concurrency,然后看看是否可以带来更多的吞吐
    • 降低t1, t2, 依然满足SLO,增大max_coneuccency,吞吐自然上去
      • 硬件
        • 融合算子,tune gemm,优化算子等,提高硬件算力利用率
        • overlap device不同单元,比如通信和计算overlap,不同算子之间的overlap等,提高硬件整体利用率
      • 框架
        • cpu端:异步cpu操作,减少cpu开销,消除硬件idle时间
        • cpu端:python is slow,减少调用栈,使用nump代替list等
        • workload: 量化,稀疏,更加细粒度的并行切分等 (能做但是不一定让做)
        • 算法:提高mtp接受率,mean_acceptance_length增大,t2自然减少,其他近似算法等
        • kv cache层级管理,空间换时间
      • 通信
        • 机内 / 机间通信,带宽利用率提升,通信算法优化等
     
     
    Loading...