3、vLLM核心概念:PagedAttention原理、Continuous Batching、KV Cache管理、vLLM的架构与组件
聊到vLLM,绕不开的就是那几个核心概念。说实话,我第一次接触PagedAttention时,脑子里想的是「这不就是操作系统的虚拟内存吗?」后来仔细一看,还真是异曲同工。这一章,我就把vLLM的几根顶梁柱拆开来讲,包括PagedAttention、Continuous Batching、KV Cache管理,还有整体架构。
3.1 为什么需要PagedAttention?
先说说背景。大模型推理时,Transformer的每一层都会产生KV Cache。这个Cache有多大呢?我举个例子:一个70B的模型,如果输入序列长度是2048,batch size是16,那KV Cache可能要吃掉几十GB的显存。
问题在于,传统的实现方式是把KV Cache当作一个连续的大块内存来分配。你想想看,每个请求的长度不一样,有的长有的短,但系统得按最大可能长度去预留空间。这就造成了严重的碎片化和浪费。
核心痛点:显存利用率低,大量空间被闲置。我曾经在一个项目中,发现KV Cache的利用率只有40%左右,剩下的全浪费了。
3.2 PagedAttention原理
PagedAttention的灵感来自操作系统的分页内存管理。它把KV Cache切分成固定大小的「块」(Block),每个块可以独立分配和释放。请求的KV Cache不再需要连续存储,而是通过一个「块表」(Block Table)来映射。
具体来说,PagedAttention做了这几件事:
- 分块存储:每个Block通常包含固定数量的token(比如16个或32个)的KV值。
- 逻辑到物理的映射:每个请求维护一个逻辑块表,指向物理显存中的实际块。
- 按需分配:只有真正用到的块才分配物理显存,避免了预分配浪费。
- 共享机制:多个请求可以共享相同的物理块(比如在Beam Search或Prefix Caching场景下)。
我的经验:在实际压测中,PagedAttention能把显存利用率从40%提升到90%以上。尤其是长序列场景,效果非常明显。
下面这张图展示了PagedAttention的核心逻辑:
3.3 Continuous Batching(连续批处理)
传统的批处理方式是「静态批处理」:等一批请求凑齐了,一起推理,一起返回。这有个问题——如果某个请求的生成长度特别长,其他短请求就得干等着。
Continuous Batching的思路是:在推理过程中动态调整批次。每生成一个token,就把已经完成的请求踢出去,把新来的请求加进来。说白了,就是让GPU一刻都不闲着。
关键点:Continuous Batching要求推理引擎支持「序列级」的调度,而不是「请求级」的调度。vLLM通过PagedAttention的块级管理,天然支持这种细粒度的调度。
我举个例子:假设当前批次有4个请求,其中2个已经生成了结束符。传统做法是等剩下2个也完成,再一起返回。但Continuous Batching会立即把完成的2个踢出,换入2个新请求。GPU的利用率从60%直接拉到95%以上。
避坑指南:我曾经在压测时发现,如果请求的生成长度差异太大(比如有的生成10个token,有的生成1000个),Continuous Batching的效果会打折扣。建议在业务层做一下长度分组,效果更好。
3.4 KV Cache管理
KV Cache的管理是vLLM性能的关键。说白了,就是怎么高效地存、怎么快速地取。
vLLM的KV Cache管理有几个要点:
- 预分配策略:启动时预先分配一大块显存作为Block Pool,避免运行时频繁分配。
- 内存复用:请求结束后,它的物理块会被回收,放入空闲列表,供后续请求使用。
- Copy-on-Write:当多个请求共享同一个物理块时,如果某个请求需要修改该块,会先复制一份再修改,避免影响其他请求。
- Swap机制:当显存不足时,可以把不常用的块换到CPU内存。虽然慢,但总比OOM强。
| 管理策略 | 作用 | 我的评价 |
|---|---|---|
| 预分配Block Pool | 减少运行时分配开销 | 必须做,不然启动后性能会抖动 |
| 内存复用 | 提高显存利用率 | PagedAttention的天然优势 |
| Copy-on-Write | 保证共享安全 | Beam Search场景下特别有用 |
| Swap to CPU | 避免OOM | 能不用就不用,CPU->GPU传输很慢 |
注意:Swap机制虽然能救急,但会显著增加延迟。我在压测时遇到过因为频繁Swap导致TP99飙升的情况。建议通过调整max_num_seqs和max_model_len来避免触发Swap。
3.5 vLLM的架构与组件
vLLM的整体架构,我习惯把它分成三层:
- 调度层(Scheduler):负责管理请求队列、决定哪些请求进入当前批次、分配和回收KV Cache块。
- 执行层(Executor):负责实际的模型推理,包括Attention计算、FFN计算等。vLLM支持多种后端,比如CUDA、ROCm、TPU。
- 服务层(API Server):提供HTTP/gRPC接口,接收请求、返回结果。支持流式输出和批处理。
下面这张图展示了vLLM的组件关系:
vLLM的组件之间通过事件驱动的方式协作。调度层收到新请求后,会向Block Manager申请物理块,然后通知执行层开始推理。推理过程中,Cache Engine负责管理KV Cache的读写和复用。
我的习惯:在部署vLLM时,我会重点关注调度层的配置,比如max_num_seqs、max_model_len、gpu_memory_utilization。这几个参数直接影响吞吐量和显存利用率。调好了,性能能翻倍。
嗯,这一章的内容就到这里。PagedAttention、Continuous Batching、KV Cache管理,这三者环环相扣,共同构成了vLLM高性能推理的基石。理解了这些,后面讲压测配置和调优时,你就能明白每个参数背后的逻辑了。
一句话总结:PagedAttention解决了显存碎片化问题,Continuous Batching榨干了GPU利用率,KV Cache管理保证了内存的高效周转。三者缺一不可。