LLM推理引擎内存管理革命的深度工程剖析:从vLLM PagedAttention到SGLang RadixAttention
引言:KV Cache——LLM推理的隐性成本
当我们在讨论大语言模型(LLM)的推理性能时,注意力往往集中在GPU算力、模型精度和并行策略上。然而,随着模型规模爆炸式增长和请求并发度的提升,内存管理正成为制约推理吞吐的核心瓶颈。在大规模LLM服务部署中,KV Cache的内存占用往往远超模型参数本身,达到GPU显存的60%~80%。如何高效地管理这块隐形巨兽,直接决定了服务的成本上限和响应延迟。
本文将深入剖析目前LLM推理领域两项最具革新性的内存管理技术:vLLM的PagedAttention与SGLang的RadixAttention。前者借鉴操作系统虚拟内存的分页机制解决KV Cache的碎片化问题,后者引入Radix Tree数据结构实现请求间的智能前缀共享与缓存复用。两者分别代表了工程优化的两个不同维度——消除浪费与最大化复用,共同推动LLM推理服务从实验室走向工业级生产。
第一部分:背景——Transformer推理中的KV Cache机制
1.1 自回归生成的注意力计算
在Transformer的自回归生成过程中,每一层的多头自注意力机制在计算当前Token的注意力时,需要访问所有历史Token的Key和Value向量,同时只需基于当前Token的Query向量。为了避免对每个新Token都重新计算全部历史Token的K、V,推理引擎在第一次"预填充"(Prefill)阶段为整个输入序列计算出所有Token的K、V,并缓存起来。后续每个"解码"(Decode)步骤只需计算新Token的Q,与缓存中的K做注意力分数计算,再加权求和V即可。这个缓存结构就是KV Cache。
KV Cache的大小可以精确计算。对于一个层数为L、隐藏维度为d、注意力头数为h、每个头维度为dsub=d/h的Transformer模型,单个Token在单层中需要存储2×h×dsub=2d个元素(K和V)的缓存。若序列长度为S,批次大小为B,每个元素占2字节(FP16/BF16),则全局KV Cache总量为:
Memory_KV = 2 × B × S × L × d × sizeof(dtype)
以一个拥有32层、隐藏维度4096的7B模型(如Llama-2-7B)为例,当批次中累计序列长度达到65536(例如128个并发请求×平均长度512)时,KV Cache在BF16精度的显存占用约为:2 × 65536 × 32 × 4096 × 2 bytes ≈ 32 GB,已经超过了模型参数本身(约14 GB @ BF16)。
1.2 传统静态分配的问题
早期的LLM推理框架(如HuggingFace Transformers、FasterTransformer等)普遍采用连续内存预分配策略:为每个请求的KV Cache在GPU显存中预留最大序列长度的空间。这种做法带来了严重的三个问题:
第一,内部碎片化。实际序列长度高度不均匀——短序列只用了一小部分预分配空间,剩余空间长期闲置。研究表明,在生产环境中,传统静态分配平均有超过20%的KV Cache显存被浪费。
第二,外部碎片化。随着请求的动态完成和释放,显存中散布着大量不连续的小空闲块,导致新到达的长序列请求无法找到足够的连续空间,即使总空闲显存充足。
第三,内存利用率低下导致的吞吐损失。由于KV Cache的大量浪费,单卡能够同时处理的并发请求数量被严格限制。在A100 80GB上部署7B模型时,仅KV Cache一项就可能占据70GB+的显存,留给新请求的空间极为有限,GPU的计算单元因数据不足而大量空闲。
第二部分:vLLM PagedAttention——将OS分页思想引入KV Cache
2.1 核心思想:从连续到分页
vLLM(UC Berkeley,2023年OSDI发表)的核心创新PagedAttention,直接借鉴了操作系统中虚拟内存与分页(Paging)的经典设计。在操作系统中,程序的虚拟地址空间被划分为固定大小的页(Page),物理内存被划分为同样大小的页框(Frame)。页表负责将虚拟页号映射到物理页框号,使得进程看到的是连续虚拟地址,但实际物理存储可以是非连续分散的。
PagedAttention将KV Cache的逻辑结构从"连续数组"重塑为"分页结构"。每个请求的KV Cache被划分为若干个固定大小的块(称为Logical Block),每个Logical Block包含固定数量Token的K/V值(例如16个Token一组)。系统维护一张Block Table(类似OS页表),记录每个Logical Block号到物理Block号(即显存中的KV Cache块索引)的映射关系。
2.2 数据结构详解
一个PagedAttention系统中的KV Cache管理涉及以下核心数据结构:
- Physical KV Blocks:GPU显存上等大小(如16 tokens × L × h × d_head × 2 bytes per token)的连续内存块池。总块数由可用显存和块大小决定。
- Block Allocator:维护空闲Block列表(Free List),提供allocate()和release()接口,通常用链表实现,O(1)分配/释放。
- Block Table(per request):每个请求独占的逻辑块→物理块映射数组。类似于OS进程的页表,但无多级索引——因为映射规模可控。
- Refcount Table:每个物理块的引用计数。用于写时复制(Copy-on-Write),支持多个请求共享同一物理Block(如Beam Search场景)。
2.3 物理块池大小计算
假设一块A100 GPU总显存为80 GB,模型参数占14 GB(7B @ BF16),核函数和额外开销约1 GB,临时工作空间峰值约5 GB,则可用于KV Cache的显存约60 GB。若每个Block包含16个Token,单个Block大小为:16 × 32 × 128 × 2 = 131072 bytes = 128 KB。则总物理块数为:60 GB / 128 KB ≈ 491,520个Block。这意味着系统理论上最多可管理491,520 × 16 ≈ 786万Token的KV Cache——足以支撑大规模的并发推理需求。
2.4 动态按需分配过程
PagedAttention的请求生命周期内存分配过程如下:
- 请求到达时:系统为Block Table分配初始空间(无需分配任何物理Block),因为此时KV Cache尚为空。
- Prefill阶段:输入序列开始被模型前向计算。每处理一个Token,其K/V写入KV Cache。当一个Logical Block被填满(或当前Block剩余空间不足),Block Allocator从Free List中pop一个Physical Block,更新Block Table映射,继续写入。整个过程按需动态增长,无任何预分配。
- Decode阶段生成中:每生成一个新Token,其K/V追加到当前Block。若当前Block已满,再从Free List分配新Physical Block。逻辑上序列连续增长,物理上Block可能分散在显存各处。
- 请求完成时:遍历该请求Block Table中所有已分配的Physical Block,将Refcount减1。若Refcount归零,则将该Block归还Free List,供后续请求使用。
2.5 CUDA Kernel的分页访问实现
PagedAttention真正技术含量最高的部分在于CUDA内核的高效实现。传统的多头注意力内核(如FlashAttention)假设KV在内存中是连续的,可以通过coalesced memory access实现高带宽利用。PagedAttention则需要让CUDA kernel在运行时根据Block Table动态跳转访问非连续的物理Block。
vLLM的做法是:在Decode阶段(query长度为1是当前最常见也是优化最关键的场景),PagedAttention内核以Block为粒度并行。每个CUDA Block负责若干个KV Cache Block,根据Block Table加载对应物理Block中的K/V数据,计算局部注意力,最后归约。关键点在于:
- 按Block索引加载而非按Token索引:每个线程块直接从Block Table读取Physical Block地址,通过Pointer Arithmetic定位到显存中的具体位置,coalesced加载整个Block的K/V数据。
- 避免Kernel Launch Overhead:PagedAttention内核与融合后的算子(如RMSNorm + Attention + Residual)一同调起,Block Table通过KV Cache metadata传递给内核。
- 无需显式数据拷贝和内存整理:不同于某些框架的做法需要在Block之间移动数据,PagedAttention始终坚持间接映射——Block Table是唯一真相来源,物理Block从不迁移。
2.6 消除内部碎片:近乎100%显存利用率
PagedAttention消除内部碎片的关键在于:只在逻辑Block边界处分块。也就是说,对于任意长度为S的序列,其KV Cache最多只浪费(块大小 - S mod 块大小)的空间。当块大小为16个Token时,单序列浪费上限为15个Token的缓存空间,浪费率上限为15/S。当S=1024时,浪费率约为1.5%;当S=512时约为3%;当S=128时约为12%。平均而言,生产环境中可达97%以上的KV Cache利用率,相比传统静态分配的60%提升了超过60%。
更关键的是,消除内部碎片意味着同样的GPU显存下可以服务更多并发请求。vLLM官方数据显示,在OPT-175B等大模型场景下,PagedAttention相比静态分配的吞吐提升可达2×–4×。
2.7 额外收益:内存共享与写时复制
PagedAttention还天然支持两个高级特性:
并行采样共享:在采样多个候选回答时,多个共享相同Prompt的样本请求可以共享Prefill阶段的KV Cache物理Block(Refcount+1),只在分叉点之后的分支独立分配新Block。这直接将N-sample场景的KV Cache开销从N×降为1×加上分叉后的差异部分。
Beam Search写时复制:在Beam Search中,多个Beam共享相同的前缀Block。当某个Beam需要向共享Block写入新Token时,先检查Refcount,若大于1则触发Copy-on-Write——copy一份Block,更新映射,再写入。这避免了Beam间的相互干扰,同时最大化共享前缀的长度。
第三部分:SGLang RadixAttention——基于Radix Tree的前缀缓存与复用
3.1 PagedAttention之后还有什么?
PagedAttention解决了KV Cache的碎片化和过量分配问题,但它仍然是一个请求级别的解决方案——每个请求的生命周期结束后,其KV Cache物理块被完全释放。然而在真实的生产场景中,大量请求之间共享相同或相似的前缀:
- System Prompt:几乎所有请求都以相同的系统指令开头(角色定义、背景信息、约束条件等),长度从几百到几千Token不等。
- Few-shot Examples:In-Context Learning场景下,请求中插入多个示例,前缀部分高度重复。
- 多轮对话:Chat应用中,用户追加消息时,历史轮次的KV Cache前缀完全可复用。
- RAG检索增强:检索出的文档片段和用户Query共同组成前缀,相同文档被多次Query时产生缓存价值。
每次都将这些共享前缀重新计算KV Cache,意味着对Prefill阶段的严重浪费。RadixAttention正是为系统性的前缀缓存而设计。
3.2 RadixAttention的三层抽象
SGLang提出的RadixAttention将KV Cache管理从分页层次扩展到全生命周期缓存层次。其核心设计包含三层互补的抽象机制:
第一层:Radix Tree前缀树。数据结构采用基于Token序列片段(而非字符)的通用Radix Tree。每个节点代表一段连续的Token ID序列,存储对应Token序列的完整KV Cache。树的根节点是空前缀,共享相同Token序列前缀的请求/会话,在树中共享路径上的同一组节点。
当多个请求共享长度不同的前缀时,Radix Tree通过节点分裂来维护精确匹配:例如一个节点覆盖Token序列[1,2,3,4,5],新请求仅需[1,2,3],则在Token 3处分裂,创建父节点[1,2,3]和子节点[4,5]。这样两个请求都能在树中找到各自最长匹配前缀。
第二层:LRU缓存淘汰策略。Radix Tree不可能无限增长,因此需要一套缓存淘汰策略。SGLang基于经典的LRU(Least Recently Used)策略管理Radix Tree中每个节点的生命周期。每个节点维护的时间戳记录最后访问时间(即KV Cache最后被某个请求引用的时间)。当总缓存Token数超过预设阈值时,从叶子节点开始逐层淘汰最久未访问的节点,释放对应物理显存。
LRU淘汰的粒度为Token级别的KV Cache块(而非整个请求),这是精细化管理的重要体现——共享前缀中的高频部分被保留,冷门的独立分支被淘汰。
第三层:基于引用计数的生命周期管理。即使某个节点未被LRU淘汰,也必须确保其显存不被PagedAttention的Block Allocator意外重分配。Radix Tree节点与底层PagedAttention的Block之间通过引用计数建立保护关系:只要节点引用计数大于0(即仍有活跃请求使用该缓存),其对应的物理Block就不会进入Free List。这实现了缓存层和分配层的安全互操作。
3.3 Prefill阶段与Cache Tree的匹配流程
当新请求到达SGLang时,Prefill阶段的大致流程如下:
- Tokenize与Radix Tree匹配:获取请求的Token ID序列后,在Radix Tree中搜索与当前序列的最长公共前缀(LCS)。这是O(序列长度 × 树高度)的操作,因为每个节点是Token列表(支持二分查找或线性扫描,取决于节点内Token序列长度)。
- 命中前缀的KV Cache直接复用:命中长度为P的共享前缀,意味着P个Token的KV Cache已经从Radix Tree中获取,无需GPU计算。Prefill阶段只需计算剩余S-P个Token的KV Cache。
- 未命中Token的KV Cache动态计算:对于未命中前缀部分(S-P个Token),执行标准的前向计算,生成新的KV Cache。该部分KV Cache同时写入PagedAttention的物理Block池中。
- 更新Radix Tree:将新计算的后缀Token的KV Cache作为新的Radix Tree节点插入/合并到树中。如果命中的前缀节点后存在相邻的更长Token序列,则系统从命中节点开始追加上后缀节点。
- 更新LRU时间戳:所有在匹配过程中访问的节点,更新其最近访问时间为当前时刻。
3.4 Decode阶段的复合策略
在Decode阶段,RadixAttention请求同时涉及两部分KV Cache访问:
- 缓存前缀部分(命中Radix Tree节点):这些KV Cache可能在非连续的物理Block中(因为是之前不同请求留下的),也可能部分已被LRU淘汰。对于仍能通过引用计数找到的部分,直接复用;对于已淘汰的重计算。
- 当前请求生成的Suffix部分:通过PagedAttention管理,动态分配新的Block。
Decode内核与PagedAttention的处理逻辑一致,唯一的区别在于Block Table中混排了缓存Block和动态Block。Cache Block的Refcount > 0保护Block不被覆盖,Decoder内核通过Block Table无缝访问两种Block。
3.5 真实生产负载下的缓存命中率
SGLang在多项基准测试中展示了RadixAttention的显著缓存收益:
- ShareGPT对话数据:平均System Prompt长度约400 Token,RadixAttention的Prefill KV Cache命中率超过85%。这意味着大部分请求的Prefill阶段显存和时间开销,只需计算不到15%的新Token。
- Few-shot In-Context Learning:包含多个示例的长Prompt前缀(1000+ Token)命中率达95%+,几乎完全消除Prefill计算。
- RAG场景:重复检索相同文档的Query请求,文档部分的KV Cache复用率可达99%。
- 多轮对话长上下文:第N轮对话的用户消息增量通常只有数十Token,前缀复用率>90%,每次新增Prefill时间控制在20ms以内(7B模型@A100)。
第四部分:深度对比——PagedAttention vs RadixAttention的设计哲学与工程权衡
4.1 设计哲学
- PagedAttention的核心理念是"按需分配,零浪费"。它关注的是单请求内部的内存效率问题,是如何让一个请求的KV Cache用最小的物理显存代价存储。其思想来源是操作系统的虚拟内存管理,本质是消除内部碎片。
- RadixAttention的核心理念是"共享复用,最大化利用"。它关注的是请求之间的缓存价值挖掘,是如何让已被计算过的KV Cache在新请求到来时继续发挥价值。其思想来源是OS的页缓存(Page Cache)和Buffer Cache,本质是减少重复计算。
4.2 实际工程中的协同关系
PagedAttention和RadixAttention在实际框架中并不是互斥替代关系,而是互补协同的。SGLang本身在底层仍然使用PagedAttention(或其变体)管理单个请求的KV Cache物理分配,RadixAttention则是在此之上构建的一层缓存逻辑。两者的架构关系可以这样理解:
- RadixAttention决定什么需要缓存(缓存策略层):哪些请求的前缀值得保留、何时淘汰。
- PagedAttention决定如何物理存储(显存分配层):具体的Block映射、物理内存管理、CUDA kernel访问。
可以这样类比:PagedAttention是操作系统的物理页框分配器(Buddy System + Slab Allocator),RadixAttention是操作系统的页缓存机制(Page Cache),两者在不同的抽象层次工作,共同贡献于整体的内存管理效率。
4.3 性能影响的关键维度
| 维度 | PagedAttention贡献 | RadixAttention贡献 | 协同效果 |
|---|---|---|---|
| Prefill延迟 | 间接贡献(更快Block分配) | 主要收益(跳过长前缀Prefill) | 减少70-95%的Prefill时间(含缓存命中和快速分配) |
| Decode延迟 | 主要贡献(Attention Kernel非连续访问优化) | 辅助贡献(缓存部分Decode内核加速) | 贡献相当,控制在5%以内 |
| 请求并发度 | 主要贡献(消除碎片→更多可用显存) | 辅助贡献(前缀缓存释放显存给更多并发请求) | 提高2-4×的总体吞吐 |
| 显存利用率 | 贡献巨大(消除单请求内部碎片) | 贡献巨大(消除请求间重复缓存) | 综合利用率可能超过95% |
4.4 局限性与挑战
两项技术也在不同的方向面临挑战:
- PagedAttention:a) Kernel实现复杂性:Attention Kernel支持分页带来额外indirection开销,虽然被显存节省的收益覆盖,但延迟层面确有1~3%的退化(相比理想连续的Flash2实现);b) 小序列块的代量开销:若Block太小(如1Token),Block Table的间接寻址代价较大,需要在Block Size和间接开销间权衡。
- RadixAttention:a) Radix Tree的内存消耗本身:树结构维护在CPU内存/主机内存中,Token序列存储需要额外的主机RAM。超大规模共享前缀树可能占用数GB的CPU内存;b) LRU复杂度:需要在每次请求结尾更新Radix TreeLRU时间戳,当请求并发度超高(千级+且频繁短请求),可能成为CPU侧瓶颈;c) 缓存一致性问题:当动态启用/禁用某些模型层或KV量化(如FP8 KV Cache)时,缓存可能失效,需要广播性淘汰。
第五部分:工程实践——如何在生产部署中最佳使用这两项技术
5.1 PagedAttention参数调优
- num_gpu_blocks配置:这是vLLM中预留GPU Block数的关键参数。配置过大则浪费GPU显存给KV Cache但减少可用于激活/临时工作区的空间;配置过小则并发能力受限。建议在生产中设置约70%-80%的可用显存用于KV Cache,其余留给激活和临时缓存。
- --enable-prefix-caching:vLLM 0.4+版本已实装类似SGLang的RadixAttention前缀缓存。开启后,生产环境Prefill阶段的延迟下降显著,尤其是System Prompt较长的场景。建议所有服务务必开启。
- Block Size:vLLM默认和SGLang的Block Size均为16 tokens,在大多数场景下是最优的。对于极短序列(如纯提取、分类),可考虑更小的8 tokens;对于超长序列(32K+),32 tokens可能更合适。
5.2 RadixAttention参数调优
- --mem-fraction-static:SGLang中控制KV Cache占总显存的比例。多轮对话场景下可以适当提高,体现缓存的复用价值;Pure Completion场景可适当降低,给激活空间更多机会。
- Radix Tree Size Limits:可以通过配置限制Radix Tree的最大Token数以控制CPU内存消耗。当怀疑CPU侧缓存树内存过大时,设置合理的上限并观察缓存命中率变化。
- 启用量化KV Cache(INT8/FP8):RadixAttention缓存的KV Cache如果启用量化(如FP8 KV Cache),缓存淘汰时也要注意维护量化状态的缓存,使新旧请求对KV Cache精度的期望一致。
5.3 监控指标建议
在生产中运行PagedAttention + RadixAttention的推理服务,应监控以下关键指标:
- KV Cache利用率:KV Cache Token数 / KV Cache容量上限
- RadixAttention命中率:(复用Token数 / 总Token数)× 100%
- Prefill吞吐 vs Decode吞吐:分别监控两个阶段的吞吐,判断瓶颈在哪个环节
- 请求排队时长:若请求排队增加,可能是KV Cache不足导致并发度下降
- 淘汰频率与分布:被淘汰与缓存的频率可以指导memFraction和缓存大小的比例
第六部分:前沿方向——内存管理的下一个前沿
6.1 Cross-layer KV Cache Sharing
最新研究(如Google DeepMind的Layer-Condensed KV Cache、阿里巴巴的LightKKM等)提出了跨层共享KV Cache的技术思路:Transformer上层注意力层的KV Cache与低层高度冗余,可以让多个层共享同一份KV Cache。这种跨层共享再叠加PagedAttention和RadixAttention的KV Cache管理和缓存,可能实现2×–4×的KV Cache压缩率,进一步释放显存用于扩容模型或提高并发。
6.2 完全舍弃KV Cache的模型架构
学术界也在探索从根本上消除KV Cache需求的模型架构,如RWKV(Receptance Weighted Key Value)、RetNet(Retentive Network)、Mamba(State Space Model)等。这些架构通过线性注意力或状态压缩实现O(1)的推理内存,无需存储完整的KV Cache。但目前这些架构在模型能力上与Transformer仍有差距,PagedAttention+RadixAttention仍是Transformer路线的最高性价比解决方案。
6.3 异构内存分层——CPU RAM + NVMe + GPU HBM 三级缓存
未来推理系统可能将KV Cache存储在三级存储体系中:高频访问的前缀缓存驻留在GPU HBM(最高速);低频前缀缓存溢出到CPU DRAM;超长冷数据甚至溢出到NVMe SSD。RadixAttention的LRU策略将被扩展为分层LRU,在三个层面单独管理淘汰。SGLang的KV Cache Token级淘汰为此奠定了结构基础。
结论
vLLM的PagedAttention和SGLang的RadixAttention共同定义了现代LLM推理引擎内存管理的新范式。PagedAttention将OS的虚拟内存分页思想首次成功应用于GPU上的结构化缓存管理,用一张Block Table解决了KV Cache的内存碎片化。RadixAttention进一步引入LRU管理的Radix Tree,将KV Cache的生命周期从单请求剥离至全系统,基于Token前缀拓扑实现计算历史的跨请求价值复用。
两者在工程上并非替代而是互补:PagedAttention管物理,RadixAttention管逻辑。它们协同的作用让一块A100 GPU上的KV Cache空间得到"近乎零浪费+近乎无限复用"的双重优化,将LLM推理服务从"昂贵的低效"带入了"规模化量产"的新阶段。随着模型规模继续增大和服务请求压力持续提升,Memory-Centric的推理引擎设计将持续成为LLM Production的制胜关键。
本文代码级分析基于vLLM 0.6.x和SGLang 0.3.x的实现。文中部分计算为理论估算值,实际性能受硬件架构、CUDA版本、模型配置等多重因素影响,请以实测数据为准。

发表评论 取消回复