LLM 推理的性能瓶颈不在算力,而在显存带宽。理解内存层级是优化的第一步。
前置知识
核心概念:GPU 内存层级
内存层级图
从寄存器到 NVMe, 每向下走一级,容量增大、延迟升高、带宽降低 。LLM 推理的性能优化,本质上就是让数据尽量停留在上面的层级。
各层级详细参数
HBM 原理:为什么叫 "高带宽内存"
HBM(High Bandwidth Memory)不是 "传统内存的升级版",而是一种完全不同的物理结构:
3D 堆叠 :HBM 将多个 DRAM die 垂直堆叠,通过 TSV(Through-Silicon Via,硅通孔)连接。HBM3 堆叠 12 层 die,每层提供独立的 I/O 通道。
宽位宽 :HBM 使用 1024-bit 位宽(DDR5 只有 64-bit),相当于同时传输的数据宽度是 DDR 的 16 倍。
带宽计算 :带宽 = 位宽 乘以 频率。HBM3 以 5.2 Gbps 运行,1024-bit 位宽,带宽 = 5.2 Gbps 乘以 1024 / 8 = 665 GB/s per stack 。H100 SXM 有 5 个 HBM3 栈,总带宽 = 3.35 TB/s 。
代价 :HBM 成本极高(比 GDDR 贵 3-5 倍),且容量受限(堆叠 12 层是极限,80GB 已是上限)。
对比 :
带宽差距是 2-67 倍,这就是为什么同样 24GB 显存,RTX 4090 的推理速度只有 A100 的一半。
Shared Memory 编程模型
Shared Memory 是 GPU 程序员唯一可以直接控制的缓存层级(L2 和 HBM 由硬件自动管理):
Shared Memory vs L1 Cache : Hopper 架构中,L1 和 Shared Memory 共用 128KB 物理空间,可以动态配置:
合理配置这个比例是 CUDA kernel 优化的重要技巧。
内存合并访问(Memory Coalescing)
GPU 显存访问有一个关键要求: warp 内 32 个线程的内存访问地址必须是连续的 ,这样才能合并为一次事务。
坏:线程 0 访问地址 0, 线程 1 访问地址 1000, ..., 线程 31 访问地址 31000 -> 32 次独立的内存事务,性能差 32 倍
这就是为什么矩阵乘法要用特定的内存访问模式(行优先 vs 列优先)。在推理中,如果自定义算子性能差,第一个要检查的就是内存访问是否合并。
KV Cache 的精确显存计算
KV Cache 是 LLM 推理中最大的动态显存消耗。精确计算如下:
以 Llama-3-70B 为例(FP16,batch=32,seq_len=4096):
这意味着 70B 模型在较大 batch 和较长序列下,仅 KV Cache 就需要 80GB,加上 140GB 权重,总计 220GB,至少需要 3 张 A100-80G 。这也是为什么实际部署中需要限制 max_model_len 和 max_num_seqs。
KV Cache 优化:PagedAttention 的显存节省
vLLM 的 PagedAttention 通过分页管理 KV Cache 实现显存优化:
部署视角:LLM 推理的显存分配
静态 vs 动态显存
显存碎片化问题
什么是显存碎片?
GPU 显存分配器(如 CUDA malloc)在频繁分配和释放不同大小的内存块后,会产生碎片:总剩余显存够,但没有足够的连续空间分配新的块。
典型场景 :
显存碎片率计算 :
权重加载的显存传输瓶颈
模型从 CPU 内存加载到 GPU 显存需要通过 PCIe,这是一个常被忽略的瓶颈:
启示 :
为什么 LLM 推理是 Memory-Bound:定量分析
FLOPs vs 内存访问比
判断一个操作是 compute-bound 还是 memory-bound 的关键指标是 计算强度(Compute Intensity) :
以矩阵乘法 C = A 乘以 B 为例(A: m 乘以 k, B: k 乘以 n):
Decode 阶段的定量分析
Decode 阶段每生成一个 token:
H100 的平衡点 :
Decode 阶段只有 1 FLOP/byte,远小于 295 FLOPs/byte,因此是 纯 memory-bound 。
这意味着:即使你的 GPU 算力再强(换成 H200、甚至理论无限算力),decode 速度也不会变快,因为瓶颈在带宽。提升 decode 速度的唯一方法是 减少权重加载量 (量化、减少参数)。
Prefill vs Decode 对比
其中 n = sequence length, d = hidden dimension。Prefill 阶段因为要处理整段 prompt 的自注意力(O(n 平方)),计算量大;Decode 阶段每次只生成 1 个 token,计算量小但要加载全部权重。
面试视角
进阶问题
"Shared Memory 和 L1 Cache 有什么区别?"
"什么是内存合并访问(Memory Coalescing)?"
"KV Cache 的显存怎么精确计算?"
常考问题
最佳实践