LLM 首字延迟 (TTFT) 优化全栈指南 #
“In interactive systems, the speed of the first response is not a performance metric — it is a trust signal. If the user waits too long, they assume the system is broken, not thinking.” —— 改编自 Jakob Nielsen, Response Times: The 3 Important Limits [1]
一、TTFT 的定义与核心公式 #
1.1 什么是 TTFT? #
TTFT (Time To First Token) 指从客户端发出请求,到模型生成并返回第一个 Token 的端到端时间。
在 LLM 的自回归生成过程中,推理分为两个截然不同的阶段:
- Prefill (预填充):一次性计算整个 Prompt 的 KV Cache。计算密集,复杂度 $O(N^2)$。
- Decode (解码):自回归逐个生成 Token。访存密集(Memory-bound),复杂度 $O(N)$。
TTFT 几乎完全由 Prefill 阶段决定。
1.2 TTFT 的核心组成 #
用户点击发送 ─────────────────────────────────────────────────────────────► 看到第一个字
│ │ │ │
▼ ▼ ▼ ▼
[ 网络传输 ] [ 排队等待 ] [ 调度分配 ] [ Prefill 计算 ]
(T_network) (T_queue) (T_sched) (T_prefill)
─────────────┴───────────┴────────────┘
端到端 TTFT
| 组件 | 含义 | 典型占比 | 优化方向 |
|---|---|---|---|
| $T_{queue}$ | 请求在队列中等待的时间 | 0% ~ 80% (高并发时) | 连续批处理、弹性扩缩容 |
| $T_{network}$ | 网络传输与协议握手延迟 | 5% ~ 15% | HTTP/2、连接池、网关优化 |
| $T_{scheduling}$ | 调度器分配 GPU 资源的时间 | < 5% | 异步调度、资源预热 |
| $T_{prefill}$ | 模型计算 Prompt 的 KV Cache | 40% ~ 90% | Flash Attention、Chunked Prefill、PD 分离 |
用户体验的心理阈值:
- < 100ms:即时响应,用户感觉“直接操作”。
- 100ms ~ 500ms:流畅,用户能感知轻微延迟但可接受。
- > 2000ms:用户产生焦虑,可能重复提交或放弃。
二、TTFT 的核心瓶颈拆解 #
2.1 Prefill 的计算与访存瓶颈 #
GPU SRAM (高速缓存)
┌─────────────────┐
│ │◄───── 权重加载 (HBM -> SRAM)
│ Compute │ (140GB for 70B)
│ (ALU Units) │
│ │
└────────┬────────┘
│
▼
┌─────────────────┐
│ │
│ HBM │◄───── 显存带宽瓶颈
│ (3.35 TB/s) │ 数据搬运 > 实际计算
└─────────────────┘
在 Prefill 阶段,尽管 FLOPS 很高,但现代 GPU (H100/A100) 的算力远超显存带宽。Prefill 阶段大部分时间在等待数据从 HBM 搬运到 SRAM,而非实际计算。
2.2 排队延迟(The Hidden Killer) #
传统推理服务使用 Static Batching:一个 Batch 填满后一起送入 GPU,Batch 中所有请求必须等待最长的那个 Prefill 完成才能返回。
传统 Static Batching (木桶效应):
───────────────────────────────────────────────────────────── 时间
Req A (短): [ P ============== ] [ D D D D ]
Req B (长): [ P ================================ ] [ D D D ]
Req C (中): [................... 等待 ...................] [ P =========== ] [ D D ]
▲
Req C 的 TTFT 被 Req B 严重阻塞
在高并发下,$T_{queue}$ 成为 TTFT 的主导因素。
三、推理引擎层优化(核心战场) #
3.1 连续批处理 (Continuous Batching) #
vLLM 的核心创新,彻底打破 Static Batching 的木桶效应。只要 Batch 中有任何一个请求完成 Decode 腾出 Slot,调度器立刻将排队的请求插入,并执行其 Prefill。
Continuous Batching (动态插空):
───────────────────────────────────────────────────────────── 时间
Req A (短): [ P ] [ D D D D ]
Req B (长): [ P ==== ] [ D D D ]
Req C (中): [ P === ] [ D D D ]
Req D (短): [ P ] [ D D D ]
▲
所有请求几乎同时开始 Prefill,TTFT 显著降低
效果:$T_{queue}$ 趋近于 0,GPU 利用率从 30% 提升至 80%+。
3.2 块级预填充 (Chunked Prefill) #
Continuous Batching 解决了排队问题,但单个超长请求仍会阻塞整个 GPU(因为 Prefill 不可中断)。
Chunked Prefill 机制:
───────────────────────────────────────────────────────────── 时间
Req Long (32k): [ P1 ] [ P2 ] [ P3 ] [ P4 ] [ P5 ] [ P6 ] ...
Req Short (A): [ P == ] [ D D ]
Req Short (B): [ P == ] [ D D ]
▲
长 Prompt 被切块 (Chunk),与短请求的 Decode 交替执行
- 原理:将长 Prompt 拆分为固定大小的 Chunk(如 512 Token)。每次迭代只计算一个 Chunk,并与 Decode 请求交替执行。
- 效果:长尾延迟(P99 TTFT)下降 60-80%。
3.3 KV Cache 复用:Prefix Caching & RadixAttention #
在实际业务中(如多轮对话、Agent 工具调用),大量请求共享相同的系统 Prompt。
RadixAttention 缓存树 (Trie):
[Root]
/ \
[SysPrompt]
/ \
[User_A] [User_B]
/ \ / \
[History] [New] [History] [New]
▲ ▲ ▲
└──────────┴──────┘
直接复用已计算的 KV Cache
T_prefill ≈ T_load_cache (< 5ms)
效果:命中前缀的请求,$T_{prefill}$ 可降至极低,因为跳过了繁重的矩阵乘法。
四、系统架构层优化(资源调度) #
4.1 PD 分离架构 (Prefill-Decode Disaggregation) #
2024-2025 年工业界最热门的架构演进。将 Prefill(计算密集)和 Decode(访存密集)拆分到不同实例。
┌──────────────────────────────────────────────────────────────┐
│ API Gateway │
│ (路由逻辑: Prompt 长度 > 8k 走 Prefill 集群,否则走 Decode) │
└───────────────┬────────────────────────────┬─────────────────┘
│ │
(长 Prompt / 计算密集) (短 Prompt / 访存密集)
│ │
▼ ▼
┌──────────────────────┐ ┌──────────────────────┐
│ Prefill Cluster │ │ Decode Cluster │
│ (H100 × 4, 高算力) │ │ (L40S × 8, 高显存) │
│ │ │ │
│ [ P1 ] [ P2 ] [ P3] │ │ [ D1 ] [ D2 ] [ D3] │
│ [ P1 ] [ P2 ] [ P3] │ │ [ D1 ] [ D2 ] [ D3] │
│ [ P1 ] [ P2 ] [ P3] │ │ [ D1 ] [ D2 ] [ D3] │
└──────────┬───────────┘ └──────────▲───────────┘
│ │
│ KV Cache Transfer │
└────────────────────────────┘
(RDMA / P2P / Shared Mem)
优势:
- 资源匹配:Prefill 吃算力,Decode 吃显存带宽。不再让昂贵的 H100 被 Decode 的低带宽利用率浪费。
- 隔离干扰:长 Prompt 的 Prefill 不再抢占 Decode 的 GPU 时间片。
- 弹性扩缩容:可根据业务峰谷独立扩容 Prefill 或 Decode 节点。
代表项目:DistServe, Splitwise, Mooncake, vLLM (PD Disaggregation)。
4.2 Flash Attention 2/3 技术 #
传统 Attention 实现需要多次读写 HBM,Flash Attention 通过 IO-Aware 分块计算,将中间结果保留在 SRAM 中。
| 版本 | 核心优化 | 适用硬件 | Prefill 提速 |
|---|---|---|---|
| Flash Attention 1 | 分块计算,减少 HBM 读写 | A100/V100 | 1.5x - 2x |
| Flash Attention 2 | 优化线程块调度,减少同步 | A100/H100 | 2x - 3x |
| Flash Attention 3 | Hopper TMA 异步加载指令 | H100/H200 | 3x - 5x |
五、常见坑点与避坑指南 #
| # | 坑点 | 现象 | 根因分析 | 解决方案 | 严重程度 |
|---|---|---|---|---|---|
| 1 | 长 Prompt 阻塞全局 | P99 TTFT 飙升,短请求排队 > 2s | 静态 Batch 或未开启 Chunked Prefill | 开启 --enable-chunked-prefill,设置合理 Chunk 大小 |
🔴 致命 |
| 2 | 显存碎片化 | 请求被 OOM 拒绝或频繁触发 Swap | 传统 KV Cache 分配非连续 | 使用 vLLM/SGLang 的 PagedAttention | 🔴 致命 |
| 3 | TP 并行度过大 | 算力利用率低,通信占比 > 30% | AllReduce 同步延迟抵消计算收益 | 控制 TP≤4(A100)或 ≤8(H100) | 🟠 高 |
| 4 | KV Cache 未清理 | 显存泄漏,运行数小时后 OOM | 异常请求或超时连接未释放 Cache | 配置 TTL + 定期 GC,启用 Paged Cache | 🟡 中 |
| 5 | 网关缓冲流式响应 | 首字延迟高,但后续输出极快 | 网关等待整个响应打包后才下发 | 开启 SSE 实时透传,禁用响应缓冲 | 🟠 高 |
六、总结与实战建议 #
6.1 分层落地路线图 #
| 场景 | 推荐技术栈 | 预期 TTFT | 实施成本 |
|---|---|---|---|
| 轻量级/原型 | vLLM + FlashAttn2 + PagedAttention | 500ms ~ 1s | 低(开箱即用) |
| 高并发生产 | vLLM/SGLang + Chunked Prefill + RadixAttention | 200ms ~ 500ms | 中(需调参) |
| 极致低延迟/长文本 | PD 分离 + Speculative Prefill + H100/L40S 异构 | < 200ms (128k) | 高(架构改造) |
6.2 给架构师的 3 条核心建议 #
- 先开 PagedAttention 和 Continuous Batching:这是 vLLM/SGLang 的默认能力,不花一分钱硬件成本,可解决 80% 的排队和碎片问题。
- 长文本必开 Chunked Prefill:不要相信"GPU 算力够就能硬扛长 Prompt"。Chunk 是保证 P99 延迟不雪崩的唯一工程手段。
- TTFT 与 Throughput 是 trade-off:过度优化 TTFT(如极小 Chunk)会牺牲吞吐。根据业务 SLA 设定合理的 Chunk Size。
文档版本:v2.0 (图文并茂版) | 作者:小伟 | 日期:2026-07-29