TTFT & TPOT & Throughput —— LLM推理性能指标
引言:延迟与吞吐的“不可能三角”
大模型推理服务上线前,业务方通常会问三个问题:快不快?贵不贵?稳不稳?
这三个问题对应着推理系统的三大核心指标:
- TTFT(首Token延迟) ——决定了用户“第一印象”
- TPOT(每输出Token延迟) ——决定了对话“流畅感”
- Throughput(吞吐量) ——决定了服务“性价比”
但糟糕的是,这三者之间存在内在的Trade-off。想提高吞吐量(多塞请求),TTFT和TPOT就会变差;想优化尾延迟(P99稳定),可能得牺牲一些吞吐量。
更复杂的是,LLM推理的两阶段特性(Prefill和Decode)让性能分析变得极其微妙——同一个系统在不同阶段的瓶颈完全不同。
本文建立一个完整的性能指标分析框架:从硬件物理极限出发,推导各指标的数学表达式,再结合调度策略分析其权衡关系。这是推理引擎性能调优的“尺子”。
一、推理两阶段的物理本质
要理解指标,必须先理解硬件在干什么。
LLM推理分为两个阶段,瓶颈完全不同:
1.1 Prefill(预填充阶段)
- 做什么:一次性处理整个输入Prompt,计算所有token的KV Cache
- 计算量:约
2 × prompt_len × hidden_dim × num_layersFLOPs - 瓶颈:计算密集型(Compute-bound) ——大量矩阵乘法(GEMM)占满Tensor Core
- 关键资源:FLOPs(浮点运算能力)
1.2 Decode(解码/自回归阶段)
- 做什么:逐个生成新token,每次生成依赖全部历史KV Cache
- 计算量:小(主要是一个
[1 × hidden_dim] × [hidden_dim × vocab]的矩阵乘) - 瓶颈:访存密集型(Memory-bandwidth-bound) ——每次前向需加载完整模型权重(例如70B ~ 140GB)
- 关键资源:HBM带宽(而非FLOPs)
1.3 物理极限公式
对于Decode阶段,单个token生成的理论最低延迟由HBM带宽决定:
Ttokenmin=HBM BandwidthModel Size
对于LLaMA-70B(FP16,~140GB),在H100(HBM带宽 3.35 TB/s)上:
Ttokenmin=3.35TB/s140GB≈42ms
这意味着,单条请求在H100上每秒最多生成约24个token(1/0.042s)。这是物理极限,任何优化(FlashAttention、量化)的本质都是降低“有效模型大小”或提升有效带宽。
但是,如果Batch中有N条请求,模型权重只需要加载一次,可以同时为N条请求计算,因此总吞吐量随N线性增长,而单条请求的TPOT会随N近似线性增加(因为计算量增加了,但带宽不变)。
这就是推理调度的核心矛盾。
二、TTFT(Time To First Token):首Token延迟
2.1 定义与SLA
TTFT定义为:从用户发送请求(或开始处理)到收到第一个输出Token的时间间隔。
包含:网络传输 + 调度排队等待 + Prefill计算 + (可能的)首次Decode。
在在线服务中,TTFT直接影响用户留存率。业界常见的SLA(服务等级协议)目标:
- 聊天机器人:TTFT < 500ms(最佳体验)
- 实时搜索摘要:TTFT < 200ms
- 离线批处理:无严格要求
2.2 TTFT的数学构成
TTFT主要由Prefill延迟决定。对于输入长度为 P 的Prompt:
TTTFT≈Tsched+GPU FLOPs×Utilization2×P×H×L
其中 H 为隐藏维度,L 为层数。
关键洞察:TTFT随Prompt长度P线性增长。处理10万token的文档,Prefill可能需要数秒——这也是Chunked Prefill诞生的直接原因(把长Prefill切碎,不让一个长请求堵住所有短请求的TTFT)。
2.3 如何优化TTFT?
| 优化手段 | 作用机制 | 效果 |
|---|---|---|
| Chunked Prefill | 长Prefill切块交织,避免队头阻塞 | 短请求TTFT降低10-50% |
| 前缀缓存(RadixAttention) | 跳过公共Prompt的重复Prefill计算 | 共享前缀场景TTFT降低5-10倍 |
| 提升GPU算力(H100→B200) | 直接降低FLOPs耗时 | Prefill延迟线性下降 |
| 降低Batch中Prefill请求数 | 减少并发Prefill竞争 | 单个TTFT改善但吞吐下降 |
三、TPOT(Time Per Output Token):每个输出Token的时间
3.1 定义与用户感知
TPOT定义为:生成每个新Token所需的平均时间(不含首个Token)。
它是衡量“对话流畅感”的核心指标。人类阅读速度约5-7 tok/s,因此推理服务通常追求:
- > 20 tok/s:感觉流畅,可实时交互
- 10-20 tok/s:可接受,略有停顿感
- < 10 tok/s:明显卡顿,体验不佳
70B模型在H100上的单请求物理极限是24 tok/s(见上文),刚好卡在“流畅”门槛上。通过量化(INT4)将有效模型大小降到35GB,单请求TPOT可飙升至~80-100 tok/s。
3.2 TPOT的物理公式
在连续批处理中,假设一个Batch包含 B 条请求,每条请求都在并发解码。
单次前向传播耗时:
Tstep≈HBM BandwidthModel Size+KV Cache Read Size
其中KV Cache读取量约为 2×L×H×D×B×seq_len_avg×dtype_size。
对于长上下文,KV Cache读取可能超过模型权重本身。
系统级TPOT(即该Batch中每个请求生成一个token的平均时间):
TPOTsystem=Tstep
单请求感知的TPOT也是 Tstep(因为是同步批量推理,该Batch中所有请求同时获得下一个token)。
因此:Batch越大,Tstep越大,TPOT越差。
3.3 TPOT与Batch Size的权衡
我们用Python模拟一下Batch Size对TPOT和吞吐量的影响:
1import matplotlib.pyplot as plt2import numpy as np3
4# 假设H100参数5model_size_gb = 140 # 70B FP166hbm_bandwidth_gbps = 3350 # GB/s7kv_cache_per_token_mb = 0.5 # LLaMA-70B约0.5MB/token (取决于seq_len)8avg_seq_len = 20489
10def simulate_batch(batch_size):11 # 模型权重读取耗时12 weight_time = model_size_gb / hbm_bandwidth_gbps # 秒13
14 # KV Cache读取耗时 (假设所有请求平均2048长度)15 kv_total_gb = batch_size * avg_seq_len * kv_cache_per_token_mb / 102416 kv_time = kv_total_gb / hbm_bandwidth_gbps17
18 step_time = weight_time + kv_time # 秒19
20 # 每个step系统生成batch_size个token21 throughput = batch_size / step_time # tokens/s22 tpot_ms = step_time * 1000 # ms per token per request23
24 return tpot_ms, throughput25
26batch_sizes = [1, 4, 8, 16, 32, 64, 128]27results = [simulate_batch(b) for b in batch_sizes]28tpot_ms, throughput = zip(*results)29
30print("Batch | TPOT(ms) | Throughput(tok/s)")31for b, t, th in zip(batch_sizes, tpot_ms, throughput):32 print(f"{b:5d} | {t:8.1f} | {th:12.1f}")33
34# 输出示例:35# Batch | TPOT(ms) | Throughput(tok/s)36# 1 | 42.0 | 23.837# 4 | 42.6 | 94.038# 8 | 43.3 | 184.939# 16 | 44.9 | 356.640# 32 | 49.0 | 653.541# 64 | 58.8 | 1088.442# 128 | 78.4 | 1632.7关键发现:
- Batch从1到128,TPOT从42ms恶化到78ms(变慢86%),但吞吐量从24 tok/s暴涨到1632 tok/s(提升68倍)。
- 系统吞吐量随Batch size亚线性增长——KV Cache读取开销让边际收益递减。
这就是服务SLA设计的核心:在满足TPOT < 50ms(20 tok/s)的前提下,尽可能增大Batch size以提升吞吐量。
四、吞吐量(Throughput):系统的生产力
4.1 定义与计算
吞吐量通常定义为系统单位时间生成的Token总数(tok/s),也可以按请求数/秒(req/s)计。
Throughput=Total TimeBatch Size×Avg Generation Length
或者从Decode角度看:
Throughput=TstepB≈Model Size+KV CacheBB×HBM Bandwidth
4.2 吞吐量与并发数的关系
吞吐量随并发请求数增加而增加,但呈现边际递减。原因:
- KV Cache膨胀:更多并发 → 总KV Cache读取量增加 → Tstep上升
- 显存容量上限:并发数受KV Cache可用显存限制
- 调度开销:超过一定并发数,CPU调度本身成为瓶颈
典型推理引擎的吞吐量曲线呈S型:
- 低并发:GPU空闲,吞吐线性增长
- 中并发:GPU饱和,吞吐增速放缓
- 高并发:显存或调度瓶颈,吞吐趋近水平甚至下降(因Swap/抢占)
4.3 优化吞吐量的手段
| 手段 | 原理 | 代价 |
|---|---|---|
| 增大Batch Size | 分摊权重加载开销 | TPOT变差、显存占用↑ |
| 量化(INT4/FP8) | 降低模型大小,提升有效带宽 | 精度轻微损失 |
| FlashAttention | 降低KV Cache读写开销 | 无(纯算法优化) |
| 连续批处理 | 消除静态批处理的空闲等待 | 调度复杂度↑ |
五、尾延迟(Tail Latency):P95/P99的“长尾之痛”
5.1 为什么平均值欺骗人?
平均值(P50)掩盖了最坏情况。在线服务中,P99延迟决定了最慢的1%用户的体验,而这1%往往是付费大客户或高敏感场景。
假设P50 TPOT = 40ms,P99 TPOT = 200ms。意味着100个请求中,有1个用户要忍受5倍于正常速度的卡顿。
5.2 尾延迟的主要来源
在LLM推理系统中,尾延迟的根源包括:
- 请求长度极端差异:一个超长Prompt(Prefill)堵住了调度器
- 调度抖动:连续批处理的“抢占/Swap”机制本身有开销
- 显存分配延迟:PagedAttention的Block分配偶尔出现慢路径
- 硬件异构性:不同GPU之间的微小差异(电压、温度导致频率波动)
- 垃圾回收/内存管理:Python/CUDA context切换
5.3 连续批处理的“双刃剑效应”
连续批处理对尾延迟是一把双刃剑:
正面:消除了静态批处理的“木桶效应”,短请求不被长请求阻塞,整体P50大幅改善。
负面:引入了抢占(Preemption)和换入换出(Swap)的开销。当一个高优先级请求被抢占,其KV被换出到CPU内存,恢复时再换入。这个过程可能耗时几十到几百毫秒——直接体现在该请求的P99甚至P95上。
1# 模拟连续批处理中抢占对尾延迟的影响2import random3import numpy as np4
5def simulate_batch_with_preemption(num_requests, preemption_rate=0.02):6 latencies = []7 for i in range(num_requests):8 base_latency = random.gauss(45, 5) # 正常TPOT 45ms9 if random.random() < preemption_rate:10 # 被抢占:增加200-500ms的换入换出开销11 preempt_penalty = random.uniform(200, 500)12 base_latency += preempt_penalty13 latencies.append(max(0, base_latency))14
15 p50 = np.percentile(latencies, 50)16 p95 = np.percentile(latencies, 95)17 p99 = np.percentile(latencies, 99)18 print(f"P50: {p50:.1f}ms, P95: {p95:.1f}ms, P99: {p99:.1f}ms")19
20simulate_batch_with_preemption(10000)21# 输出类似:P50: 44.8ms, P95: 68.2ms, P99: 312.5ms22# P95被轻微影响,P99被抢占严重拖累5.4 优化尾延迟的实战策略
- 请求级优先级:付费用户设置高优先级,避免被抢占
- 细粒度调度:vLLM V1中缩小调度窗口,减少单次调度决策的“犯大错”概率
- 预热与缓存:RadixAttention让长尾请求(共享前缀)直接命中缓存,跳过Prefill
- 过载保护:设置最大并发数,超过阈值时拒绝新请求(或排队等待),保证在线请求稳定
- 内核级QoS:通过CUDA MPS或MIG进行物理资源隔离,防止“吵闹邻居”影响关键请求
六、综合权衡:建立一个SLA优化框架
在生产环境中,优化不是单点突破,而是在延迟、吞吐、成本之间寻找最优解。我们可以建立一个简单的约束优化模型。
6.1 优化目标
MaximizeThroughputsubject to:
TTFTp95<500ms
TPOTp95<50ms(即 20 tok/s)
显存占用<GPU_VRAM
6.2 调优决策树
11. 目标TPOT是否有余量?2 ├─ 是(远超20 tok/s)→ 增大Batch Size(提升吞吐),直到TPOT触达SLA阈值3 └─ 否(TPOT接近阈值)→ 考虑量化(INT4)降低模型大小,留出TPOT预算4
52. 目标TTFT是否达标?6 ├─ 否(长Prefill阻塞)→ 开启Chunked Prefill / 前缀缓存7 └─ 是 → 保持8
93. P99尾延迟是否过大?10 ├─ 是(受抢占影响)→ 增加显存预留 / 优化KV Cache分配策略 / 限制最大并发11 └─ 否 → 继续增大吞吐12
134. 显存是否用完?14 ├─ 是 → 降低Batch Size或启用KV Cache量化(FP8/INT8)15 └─ 否 → 继续增加并发6.3 关键经验法则
- TPOT与Batch Size近似线性关系:Batch翻倍,TPOT约增加10-30%(取决于KV占比)。可以用这个规律快速估算。
- TTFT由Prefill主导,与Batch中的Decode请求无关(在Chunked Prefill下,Decode请求不会阻塞Prefill,但会抢预算)。
- P99 ≈ P50 + 调度抖动方差:在一个设计良好的系统中,P99通常在P50的1.5-3倍之间。超过3倍说明存在严重的调度或显存问题。
七、总结:一张图看懂全部指标
1┌──────────────────────────────────────────────────────────────────────┐2│ LLM推理性能指标全景图 │3├──────────────────────────────────────────────────────────────────────┤4│ │5│ 用户体验层 系统运营层 │6│ ┌──────────────────┐ ┌─────────────────────────────┐ │7│ │ TTFT (首Token) │ │ Throughput (吞吐量) │ │8│ │ 目标: <500ms │ │ 目标: 最大化 tok/s/$ │ │9│ │ 瓶颈: Prefill │ │ 瓶颈: HBM带宽 + 调度 │ │10│ │ 优化: Chunked │ │ 优化: 大Batch + 量化 │ │11│ └──────────────────┘ └─────────────────────────────┘ |12│ │13│ ┌──────────────────┐ ┌─────────────────────────────┐ │14│ │ TPOT (解码速度) │ │ Tail Latency (尾延迟) │ │15│ │ 目标: <50ms │ │ 目标: P99 < 3×P50 │ │16│ │ 瓶颈: HBM带宽 │ │ 根源: 抢占/长尾请求 │ │17│ │ 优化: 量化 │ │ 优化: 优先级/隔离/限流 │ │18│ └──────────────────┘ └─────────────────────────────┘ │19│ │20├──────────────────────────────────────────────────────────────────────┤21│ 核心权衡: Batch Size (并发数) │22│ ┌───────────────────────────────────────────────────────────────┐ │23│ │ Batch小 → TTFT低 ✓ TPOT低 ✓ 吞吐低 ✗ 成本高 ✗ │ │24│ │ Batch大 → TTFT高 ✗ TPOT高 ✗ 吞吐高 ✓ 成本低 ✓ │ │25│ └───────────────────────────────────────────────────────────────┘ │26│ │27│ 物理极限公式: T_token_min = Model_Size / HBM_Bandwidth │28│ 实践意义: 70B FP16 × H100 ≈ 42ms → 最大 24 tok/s │29│ 意味着: 单条请求永远无法突破此极限 (除非量化/蒸馏) │30└──────────────────────────────────────────────────────────────────────┘建议:监控系统需要同时跟踪这四类指标,并建立它们之间的相关性仪表盘。当吞吐量上升时,密切监视TPOT和P99是否突破SLA红线。优化的本质,是在用户体验(延迟) 和运营成本(吞吐) 之间,找到那个最佳平衡点。而这个平衡点,是由你的业务SLA和硬件物理极限共同决定的。
Some information may be outdated