跳到主要内容
推理优化

第1章:LLM 推理基础

理解 LLM 自回归生成的 Prefill/Decode 两阶段、KV Cache 机制和推理关键性能指标

LLM推理 Prefill Decode KV Cache TTFT TPOT

LLM 训练只是万里长征第一步,真正服务用户的是推理。本章建立推理优化的全部基础概念:Prefill / Decode 两阶段为什么计算特性截然不同、KV Cache 是什么/占多少显存/怎么管理、TTFT/TPOT/Throughput/Goodput 这些指标各自衡量什么——这些是后面所有推理优化(PagedAttention、量化、Speculative、PD 解耦)的共同前提。

📑 目录


1. LLM 自回归生成机制

LLM 生成是严格的串行过程:

Prompt: "今天天气"

Step 1: 模型看 [今天, 天气] → 生成 "真"
Step 2: 模型看 [今天, 天气, 真] → 生成 "好"
Step 3: 模型看 [今天, 天气, 真, 好] → 生成 "<EOS>"

每一步生成 1 个 token,需要把所有历史 token 都”看”一遍。Naive 实现下,每步都要重新算所有历史的 Q/K/V——这就是 KV Cache 要解决的问题


2. Prefill vs Decode:计算特性对比

整个推理被切成两个阶段:

2.1 Prefill 阶段

一次性处理完整 prompt(几百到几千 token),算出所有位置的 KV Cache,生成第 1 个 token。

Input:  prompt 的所有 token (B, S_prompt)
Output: 第一个生成的 token + 完整 KV Cache (B, S_prompt, ...)

计算特性:

  • 输入是大矩阵 (BS,d)(B \cdot S, d),GEMM 矩阵很大
  • Tensor Core 利用率高
  • Compute Bound

2.2 Decode 阶段

每一步只生成 1 个 token,Q 的序列长度 = 1。

Input:  上一步生成的 1 个 token
        + 之前所有 KV Cache
Output: 下一个 token
        + 把这一步的 KV 追加到 Cache

计算特性:

  • 输入退化成小向量 (B1,d)(B \cdot 1, d),GEMM 变成 GEMV
  • Tensor Core 利用率低
  • 大部分时间在搬 KV Cache(~10 GB 量级)
  • Memory Bound

2.3 对比表

维度PrefillDecode
Q 序列长度S_prompt (大)1
算力利用率高(>50%)低(<10%)
主要瓶颈ComputeMemory
单 token 耗时~10-100 ms~10-50 ms
优化方向FlashAttention / Tensor CoreKV Cache 管理 / Speculative

🌟 核心矛盾:Prefill 算力跑满,Decode 带宽跑满——这就是 PD 解耦的动机(第 6 章详讲)。


3. KV Cache:显存刺客

3.1 为什么需要 KV Cache

每一步 Decode,Attention 需要历史所有 token 的 K 和 V。如果每步都重新算,复杂度 O(S2)O(S^2),生成 4K 长度要重算 4K × 4K 次——根本不可行。

KV Cache:把历史所有 token 的 K、V 算出来后存起来,每次新 token 只算自己的 K、V 并 append:

Step 1 之后: KV[0:S_prompt] (Prefill 算出)
Step 2:    Q_new (1 token) 算 K_new, V_new → KV[S_prompt: S_prompt+1]
           Attention(Q_new, KV[0:S_prompt+1])
Step 3:    继续追加...

3.2 KV Cache 的生命周期

分配 → Prefill 填充 → Decode 不断追加 → 请求结束 → 释放

关键挑战:不同请求的生成长度不同,显存分配是动态的、不可预测——这是 PagedAttention 要解决的核心问题。

3.3 KV Cache 的计算图

每个 layer 有自己的 KV:

KV Cache 形状: (num_layers, 2, batch, num_heads, seq_len, head_dim)
              其中 2 表示 K 和 V

4. KV Cache 显存账本

4.1 公式

KV Cache 大小=2×L×Hkv×S×D×bytes\text{KV Cache 大小} = 2 \times L \times H_{kv} \times S \times D \times \text{bytes}

(MHA 时 Hkv=HH_{kv} = H;GQA 时 Hkv<HH_{kv} < H)

4.2 LLaMA-2-7B 实例

超参
L (num layers)32
H_kv (KV heads, MHA)32
D (head dim)128
S (seq len)4096
bytes (BF16)2

单个请求 KV Cache = 2×32×32×4096×128×2=2 GB2 \times 32 \times 32 \times 4096 \times 128 \times 2 = 2 \text{ GB}

如果 batch=16,总 KV Cache = 32 GB —— 比 7B 模型本身(14 GB)还大!

4.3 长上下文场景

LLaMA-3 支持 128K 上下文:

每请求 KV=2×32×8×131072×128×2=17 GB\text{每请求 KV} = 2 \times 32 \times 8 \times 131072 \times 128 \times 2 = 17 \text{ GB}

(LLaMA-3 用 GQA,Hkv=8H_{kv} = 8)

单请求就吃 17 GB! 这就是为什么 KV 量化(KIVI、INT8)对长上下文场景至关重要。

4.4 KV Cache 占比公式

显存预算 80 GB 的卡上:

模型权重(7B BF16):       14 GB
临时 buffer / activation: ~5 GB
剩余给 KV Cache:          ~60 GB

每请求 KV(4K, MHA):      2 GB
最大并发:                 60 / 2 = 30 个请求

KV Cache 直接决定推理服务的并发能力


5. 推理性能指标全集

指标含义用户感知
TTFT (Time To First Token)首 token 延迟”等了多久才开始有反应”
TPOT (Time Per Output Token)每 token 延迟”字一个一个蹦的速度”
TBT (Time Between Tokens)同 TPOT同上
Throughput总吞吐(token/s)服务总产出
Latency端到端延迟TTFT + TPOT × output_len
QPS每秒请求数服务并发能力
P50 / P95 / P99尾延迟分布慢请求严重程度
Goodput满足 SLO 的有效吞吐真正可用的服务质量

5.1 一个典型场景

聊天应用:

  • TTFT 目标 < 500 ms(用户可接受)
  • TPOT 目标 < 50 ms(20 token/s,接近阅读速度)

如果 P95 TPOT 是 200 ms,即使平均 TPOT 50 ms 用户体验也很差——5% 请求会卡顿。

5.2 Goodput vs QPS

QPS:        每秒处理请求数
Goodput:    每秒"满足 SLO"的请求数

服务过载时 QPS 还在涨,但 latency 飙升,实际用户体验崩了——Goodput 才是真实的产出,这就是 PD 解耦优化的目标。


6. 推理请求的完整链路

客户端发起请求

HTTP / gRPC 接收

Tokenizer:文本 → token ids               (~1ms,CPU)

Scheduler 排队 / 批合并(Continuous Batching)

Prefill 阶段:模型一次性算完 prompt        (10-200ms,Compute Bound)

返回 first token (TTFT 计时点)

Decode 循环(逐 token):
   ┌──────────────────────────────────────┐
   │ 1. KV Cache 取出 + 追加新 K/V         │
   │ 2. Attention + FFN (memory bound)    │
   │ 3. Sampling(Top-p / Top-k / temp)    │
   │ 4. 返回 token 给客户端(SSE 流式)      │
   │ 5. 检查终止条件(EOS / max_len)        │
   └──────────────────────────────────────┘

请求结束,释放 KV Cache

Detokenize:token ids → 文本(增量)

每一环都有可能成为瓶颈,需要分阶段排查。


7. 采样策略

LLM 输出是概率分布,采样策略决定”选哪个 token”。

7.1 几种主流方法

方法公式/规则用途
Greedy总取最高概率确定性场景(代码、翻译)
Top-k从概率前 k 个里采控制随机性
Top-p (nucleus)累积概率达 p 的最小集合里采通用
Temperaturescale logits 调整尖锐度控制创造性
Beam Search同时维护 k 条候选序列翻译、确定性

7.2 PyTorch 实现

import torch
import torch.nn.functional as F

def sample(logits, temperature=1.0, top_k=50, top_p=0.9):
    logits = logits / temperature

    # Top-k
    if top_k > 0:
        top_k_vals, _ = torch.topk(logits, top_k)
        logits[logits < top_k_vals[..., -1:]] = -float('inf')

    # Top-p
    if top_p < 1.0:
        sorted_logits, sorted_idx = torch.sort(logits, descending=True)
        cumulative_probs = F.softmax(sorted_logits, dim=-1).cumsum(dim=-1)
        sorted_indices_to_remove = cumulative_probs > top_p
        sorted_indices_to_remove[..., 1:] = sorted_indices_to_remove[..., :-1].clone()
        sorted_indices_to_remove[..., 0] = False
        indices_to_remove = sorted_indices_to_remove.scatter(-1, sorted_idx, sorted_indices_to_remove)
        logits[indices_to_remove] = -float('inf')

    probs = F.softmax(logits, dim=-1)
    return torch.multinomial(probs, num_samples=1)

7.3 性能影响

Sampling 通常很快(< 1 ms / token),但Speculative Decoding 时温度高会让 draft 命中率骤降(第 5 章会讲)。


✅ 自我检验清单

  • 两阶段直觉:能向小白解释为什么 Prefill 快、Decode 慢
  • KV Cache 必要性:能解释不用 KV Cache 时复杂度从 O(S)O(S) 变成 O(S2)O(S^2)
  • KV Cache 算账:不查资料能算 LLaMA-2-7B 在 batch=16, S=4096 时的 KV Cache 总量(32 GB)
  • GQA 影响:能算 LLaMA-3 用 GQA 时 KV Cache 比 MHA 少多少倍
  • 指标辨析:能解释 TTFT / TPOT / Throughput / Goodput 各自衡量什么
  • 链路拆解:能从 HTTP 到 detokenize 完整画出一次推理请求的链路
  • 瓶颈定位:给定指标(TTFT 高、TPOT 高、显存满、P95 抖动),能指出最可能的根因
  • Sampling 实现:能手写 Top-k + Top-p + temperature 的采样函数
  • 并发估算:给定显存预算和模型,能算出最大并发请求数

📚 参考资料