Sealessland logo Sealessland
LLM Inference

LLM 推理框架的优化目标与手段

整理LLM推理优化到底在忙什么,避免变成名词表。

LLM 推理框架的优化目标与手段

之前我对推理框架的理解比较散,知道有 Continuous Batching、有 FlashAttention、有量化,但到底在优化什么、为什么优化这些,其实没串起来。聊完之后才觉得,还是得先回到目标上,不然容易把手段当成目的。

一个常见的误解:推理框架只改 Attention

刚开始接触这块的时候,我默认推理框架的主要工作就是加速 Attention。毕竟 Transformer 里 Attention 是 O(n2)O(n^2),看起来最吓人,FlashAttention、PagedAttention 这些名字也最响。

但其实不是。 Attention 占了计算量的显著部分,但模型参数的大头在 MLP(通常 2/3 以上),推理时的 HBM 带宽也大量消耗在读取 MLP 权重上。vLLM、SGLang 这些框架对 MLP 的优化非常多,只是性质和 Attention 不一样:

  • 算子融合:把 gate_proj、up_proj、activation、down_proj 打包成一个 kernel,减少反复读写 HBM
  • 权重量化:FP8/INT8/INT4 的 MLP 权重,降低显存带宽压力
  • MoE 优化:Grouped GEMM、专家路由 overlap、热门专家缓存
  • CUDA Graph:捕获整个 decode step,消除 CPU 调度开销

MLP 没有 KV Cache,不是因为大家懒得做,而是因为 MLP 的计算本身是无状态的——每个 token 的 MLP forward 独立,没有历史状态需要复用。Attention 的 KV Cache 是为了避免重复计算历史 token 的 Key 和 Value,MLP 不存在这个问题。

所以推理框架的优化是全栈的,不是只围着 Attention 转。

优化的四个目标

聊具体手段之前,先分清楚到底在优化什么指标。不同场景对这些指标的敏感度差别很大:

目标含义敏感场景
TTFT首 token 延迟聊天机器人、实时交互
TBT相邻 token 的生成间隔流式输出、代码补全
吞吐单位时间生成的 token 数高并发服务、批处理
显存效率同等 GPU 能塞多少并发成本敏感、长上下文

这些目标经常打架。比如增大 batch size 提升吞吐,但会恶化 TTFT;降低精度提升速度,但可能损失质量。没有万能配置,只有针对场景的权衡。

优化的四个层次

graph TD
    A[模型/算法层] --> B[算子/Kernel 层]
    B --> C[运行时/调度层]
    C --> D[并行/分布式层]
    D --> E[硬件执行]
    
    A1[KV Cache / MLA] --> A
    A2[量化 / MoE] --> A
    A3[FlashAttention] --> A
    
    B1[算子融合] --> B
    B2[CUDA Graph] --> B
    B3[Triton Kernel] --> B
    
    C1[Continuous Batching] --> C
    C2[Chunked Prefill] --> C
    C3[Prefix Caching] --> C
    
    D1[TP / PP] --> D
    D2[DP / EP] --> D

1. 模型/算法层

这一层改动的是模型本身或计算方式:

  • KV Cache:标准做法,缓存历史注意力状态
  • PagedAttention / RadixAttention:把 KV Cache 分页管理,消除显存碎片
  • FlashAttention / FlashInfer:IO-aware 的 attention 算法,减少 HBM 访问
  • MLA:压缩 KV Cache 维度,从架构上减少需要缓存的数据量
  • Speculative Decoding:小模型快速生成候选,大模型并行验证
  • 量化:FP8/INT8/INT4 权重和激活
  • MoE:只激活部分专家,降低实际计算量

2. 算子/Kernel 层

这一层让单次计算更快:

  • 算子融合:减少 kernel launch 和显存来回搬运
  • 手写 Kernel:用 Cutlass / Triton 针对特定 shape 和精度做定制
  • CUDA Graph:捕获静态计算图,消除 CPU 调度开销
  • 编译优化:Torch.compile / XLA 自动融合、算子选择

3. 运行时/调度层

这一层让请求排布更合理:

  • Continuous Batching:动态插入新请求,避免 GPU 空转
  • Chunked Prefill:把长 prefill 切成块,和 decode 混排,减少阻塞
  • Prefix Caching:复用重复的系统提示词或多轮对话前缀
  • Disaggregated Serving:把 Prefill 和 Decode 拆到不同 GPU 上,各自优化

4. 并行/分布式层

这一层横向扩展:

  • Tensor Parallelism (TP):把单层网络切开,通信量在卡间
  • Pipeline Parallelism (PP):把不同层放不同机器,流水线执行
  • Sequence Parallelism (SP):把序列维度切开,配合 Ring Attention
  • Expert Parallelism (EP):MoE 模型里不同专家放不同卡

一个容易搞混的点

我以前容易把”量化”和”KV Cache 压缩”混为一谈。量化是降低权重或激活的精度,减少数据搬运量;KV Cache 压缩是减少需要缓存的数据量(比如 MLA 把 KV 压到低维 latent space)。两者可以叠加,但解决的问题不一样。

同样,Continuous Batching 和 Chunked Prefill 也经常被混淆:

  • Continuous Batching 解决的是”GPU 不要空转”——新请求动态加入当前 batch
  • Chunked Prefill 解决的是”长 prefill 不要饿死 decode”——把 prefill 切成小段调度

写到这才发现,我之前对推理框架的理解确实是一张散的名词表。现在至少能把”目标”和”手段”分开看,也能理解为什么同样一个技术在不同场景下的效果完全不一样——比如 Continuous Batching 能提升吞吐,但会拉长 TTFT。