跳到主要内容

LLM 推理技术详解:标准机制与工业级优化

版本: 2.0 (修订版) 核心修订: 明确区分了"KV Cache 的标准推理逻辑"与"工业级系统优化"。


1. 核心机制:KV Cache 标准推理流程

只要开启了 KV Cache(这是所有现代推理引擎的默认基准),LLM 的推理过程就严格包含两个截然不同的阶段:Prefill(预填充)Decode(解码)

这是算法层面的标准行为,旨在消除重复计算。

第一阶段:Prefill (预填充 / 首词生成)

"一口气读完 Prompt,并生成第一个词"

  • 输入 (Input): 完整的提示词序列 [Token_0, Token_1, ..., Token_N-1]
  • 计算行为:
    • 全量计算: 这是一个巨大的矩阵乘法。模型并行计算所有 Token 的 Q,K,VQ, K, V
    • 填充 Cache: 将计算出的所有 KKVV 存入显存。
    • 首词预测: 利用最后一个 Token 的输出,预测生成 Token_N
  • 算力特征: 计算密集型 (Compute-bound)。GPU 利用率极高,因为是一次性并行处理矩阵。

第二阶段:Decode (解码 / 自回归生成)

"只进一个词,只算一次注意力"

  • 输入 (Input): 仅仅是 上一步生成的最新 Token [Token_N] (Shape: 1 × Dim)。
    • 注意:这里绝对不会再输入之前的 Prompt。
  • 计算行为:
    1. 算当前: 计算这 1 个 Token 的 Qnew,Knew,VnewQ_{new}, K_{new}, V_{new}
    2. 取历史: 从显存中取出之前存好的 Kcache,VcacheK_{cache}, V_{cache}
    3. 拼整体: Kall=Concat(Kcache,Knew)K_{all} = \text{Concat}(K_{cache}, K_{new})
    4. 算 Attention:
      • 使用 QnewQ_{new} (1个向量) 去查询 KallK_{all} (N+1个向量)。
      • 运算本质是 向量-矩阵乘法 (GEMV),而不是矩阵-矩阵乘法 (GEMM)。
  • 算力特征: 访存密集型 (Memory-bound)
    • 计算量非常小(只算一个词)。
    • 瓶颈在于显存带宽:需要把巨大的 Kcache,VcacheK_{cache}, V_{cache} 从显存搬运到计算核心,仅为了和这 1 个向量做乘法。

2. 为什么说这是"标准"而非"进阶"?

如下表所示,只要不是教学用的 Demo 代码,生产环境下的推理必须遵循上述逻辑。

模式输入数据形状 (Step N)Q 的计算量是否可用
朴素模式 (NanoGPT)[0 ... N] (全量)计算 NN 个 Q不可用 (速度 O(N2)O(N^2),慢且贵)
标准 KV 模式 (HuggingFace)[N] (仅当前词)计算 1 个 Q基准线 (速度 O(N)O(N),业界标准)

3. 总结对比图

维度NanoGPT (教学)标准推理 (Baseline)
核心逻辑每次重算全文Prefill + Decode
Input 形状变长 [0...t]恒定 [1]
KV Cache有 (连续显存)
瓶颈计算量爆炸显存碎片 / 带宽瓶颈
代表库minGPTtransformers

4. 标准 KV Cache 的局限性

虽然 KV Cache 解决了重复计算问题,但传统实现(如 HuggingFace Transformers)存在严重的显存管理问题

4.1 预分配浪费

假设模型的 max_len = 2048,当用户输入"你好"(2 个 Token)时:

  • 系统必须预留 2048 个 Token 的连续显存空间
  • 实际只用了几个 Token,剩余空间被锁定但空置
  • 浪费率高达 60% - 80%

4.2 显存碎片化

  • 内部碎片: 预留了没用完的空间
  • 外部碎片: 显存总量够,但因不连续无法分配给新请求

4.3 并发受限

由于每个请求都要预留大块连续显存,同一张显卡能同时服务的请求数(Batch Size)被严重限制。


如何解决这些问题?

vLLM 团队提出的 PagedAttention 技术,借鉴操作系统虚拟内存分页思想,将 KV Cache 切分为小块按需分配,彻底解决了显存碎片化问题。

👉 详见:PagedAttention:vLLM 的显存管理革命


一句话总结: Prefill 和 Decode 是 KV Cache 的物理定律(基本逻辑)