1. LLM 推理基础:先摸清底子再动手
做推理加速,第一件事不是调参数,是理解模型到底在干什么。说白了,你得知道瓶颈在哪,才知道加速什么。
Transformer 架构大家都不陌生,但我发现很多同学对推理阶段的特性和训练阶段混着看。嗯,其实差别挺大的。我今天就带你过一遍推理视角下的 Transformer,从自回归生成到真正的性能瓶颈。
1.1 Transformer 架构回顾:推理时它长这样
先看一张整体图,这是我画的一个推理阶段的 Transformer 流程。
在推理阶段,我们做的是自回归生成:每次只预测下一个 token,然后把新 token 拼到输入序列里继续预测。这个过程里,整个模型只做前向计算,没有反向传播。
每一层 Transformer Block 里面,最耗时的就是:
- Self-Attention:计算 Q 和 K 的点积,再对 V 做加权求和。序列越长,计算量平方增长。
- Feed-Forward Network(FFN):两个全连接层,参数量最大,计算密集。
我早年调第一个推理系统时,根本没关注 FFN 的权重带宽瓶颈,结果 GPU 利用率一直上不去。后来一算,单次 FFN 要加载上百 MB 的权重,内存带宽才是天花板。
1.2 自回归生成原理:一步步生成,内存爆炸就在眼前
自回归生成其实就三步循环:
- 把当前 token 序列输入模型,拿到 logits。
- 选下一个 token(greedy、top-k、top-p 等策略)。
- 把新 token 拼到序列尾,重复第一步。
这里有个关键点:每次都要重新计算所有 token 的 attention 吗?
不,优化方案是 KV Cache。在第一个 token 之后,每次只计算新 token 的 K 和 V,然后把历史 K 和 V 存下来,直接复用。这样 attention 的计算量就从 O(L²) 降到 O(L)。但我遇到过不少同学一上来就开 KV Cache,却发现显存爆炸——因为 Cache 会随着序列长度线性增长。如果你跑 4096 tokens,每层每头两个矩阵,层数 32,那光是 KV Cache 就要吃掉好几个 GB。
另外,采样策略也会影响速度。贪婪解码最简单,but 可能缺乏多样性。Beam Search 保持多个候选序列,计算量几倍翻。我个人习惯在 latency 敏感的场景只用 greedy 或 top-k(k 不超过 10),否则你加速半天白费了。
1.3 推理瓶颈分析:到底慢在哪?
我做优化时第一步永远是定位瓶颈。LLM 推理的瓶颈一般分三类,我直接给你总结成一张表:
| 瓶颈类型 | 根因 | 典型表现 | 加速方向 |
|---|---|---|---|
| 计算瓶颈 | 大量的矩阵乘法(GEMM/GEMV) | GPU ALU 利用率高,但带宽未饱和 | 算子融合、量化、剪枝 |
| 内存带宽瓶颈 | 权重和 KV Cache 的反复加载 | GPU 计算单元等待数据,利用率低 | 量化(4-bit/8-bit)、减少参数量、更小的 Cache |
| 访存模式瓶颈 | 自注意力中的随机访存(Gather/Scatter) | 大量小 kernel 启动开销 | FlashAttention、page attention、KV cache 连续存储 |
为什么说内存带宽是最大的敌人?你想想看,一次矩阵乘法要拿回整个权重矩阵,而权重文件比计算量早就在那里了。当模型变大,比如 13B、33B,单次前向推理需要从 HBM 里搬好几 GB 的数据,而计算时间反而远小于数据搬移时间。
我曾经在 3090 上跑 LLaMA-13B,batch size=1 时 GPU 利用率不到 20%。一看 nvidia-smi 的 memory utilization 一直是满的,但 compute utilization 飘忽不定。原因就是每次推理都要从 HBM 把 26GB 的权重拉一遍,而计算单元只能干等着。后来换了 4-bit 量化,权重缩小 4 倍,利用率直接翻了一倍。
1.4 瓶颈分析的实战思路
如果你拿到一个模型要优化,别急着改代码。我建议按这个顺序排查:
- 测性能基线:用
llama.cpp的--benchmark跑一次,记录 tokens/s 和内存使用。 - 算理论指标:模型参数量×2(fp16)除以带宽 ≈ 理论最小的 token 延迟。
- 对比实际延迟:如果实际延迟远大于理论值,说明有严重的计算或访存效率问题。
- 逐层 profile:用 nsys(NVIDIA Nsight Systems)或者
perf看每层的耗时。一般自注意力层和 FFN 层会占大头。 - 调整 batch size:batch size=1 时带宽瓶颈明显,增大 batch 可以让计算瓶颈变主导,但显存会涨。
--mlock 或者 --no-mmap。有些系统的内存分配策略很坑,lock 了反而增加页表开销。我遇到过几次,lock 后吞吐不升反降。
好了,这一章我们就打下这个底。Transformer 的推理流程、自回归特性、以及三个主要的瓶颈,你心里有数了。后面几章咱们就一个一个解决这些瓶颈——量化、kernel 优化、KV cache 压缩、投机解码等等。
嗯,先说这么多,先消化一下。