在 vLLM 的前缀缓存架构中,哈希索引层负责快速定位已缓存的前缀内容,而物理块管理器(Block Manager)则承担着这些内容在 GPU 显存中的实际存储、分配与淘汰职责。两个子系统协同工作,才能实现高效的前缀复用与显存利用。本文聚焦物理块管理器的内部机制,解析其块分配策略、淘汰算法以及与 GPU 显存的整体协调方式。

物理块分配机制的核心设计

vLLM 采用固定大小的块(Block)作为 GPU 显存分配的基本单元,每个物理块默认容纳 16 个 Token 的 KV 缓存数据。物理块管理器维护两类核心数据结构:逻辑块表(Logical Block Table)和物理块表(Physical Block Table)。逻辑块表位于序列级别,记录某个序列从第几个 Token 到第几个 Token 使用了哪个物理块;物理块表则位于全局级别,记录每个物理块的引用计数、最后访问时间戳以及当前内容哈希值。

当一个新的请求到达且需要扩展序列时,Block Manager 首先检查前缀缓存哈希索引是否命中。如果命中,系统会获取对应的物理块列表,并将这些物理块的引用计数加一,直接挂载到新序列的逻辑块表中,无需重新分配显存。只有当新生成的 Token 超出已缓存前缀范围时,才会触发物理块的分配流程。此时 Block Manager 从空闲块列表(Free Block List)中获取物理块,如果空闲块不足,则启动淘汰机制回收已有块。

物理块分配采用首次适应(First-Fit)策略,优先选择地址较低的空闲块,以保持显存地址的连续性。这种策略虽然简单,但在实际工作负载中表现稳定,能够有效减少显存碎片化。对于前缀缓存场景,系统还会优先尝试复用具有相同内容哈希的物理块,这是前缀缓存能够发挥作用的关键所在。

LRU 淘汰策略的实现细节

当 GPU 显存不足以容纳新的物理块时,Block Manager 必须选择某些现有块进行淘汰。vLLM 采用最近最少使用(LRU)算法作为主要的淘汰策略,具体实现包含两个关键机制:时间戳追踪与淘汰候选集筛选。

每个物理块在每次被访问(读取或写入)时都会更新其最后访问时间戳。淘汰发生时,系统首先筛选出所有引用计数为零的物理块,因为正在被活跃序列使用的块不能被淘汰。在可淘汰的块集合中,系统选择时间戳最久远的块作为牺牲目标。这种设计确保了长期未被访问的冷数据优先释放,而热点前缀得以保留。

值得注意的是,引用计数机制在淘汰决策中扮演重要角色。当多个序列共享同一物理块时(例如它们共享某个公共前缀),该块的引用计数大于一,Block Manager 不会将其列入淘汰候选集。只有当所有引用该块的序列都完成推理并释放资源后,引用计数降为零,该块才变为可淘汰状态。这种设计避免了因过早淘汰共享块而导致的缓存失效问题。

除了 LRU 策略外,vLLM 还支持按块年龄(Block Age)的淘汰变体。在某些高并发场景下,系统会优先保留创建时间较新的块,因为这些块对应的前缀更可能出现在当前活跃请求中。这种优化能够提升前缀缓存的命中率,特别是在请求模式动态变化的场景下表现尤为明显。

GPU 显存的协同管理

物理块管理器并非独立运作,而是与 vLLM 的整体显存管理子系统紧密配合。KV 缓存 Allocator 与 Block Manager 通过统一的内存分配接口进行交互,确保物理块的分配与释放与 CUDA 显存生命周期保持一致。

在初始化阶段,Block Manager 根据模型配置和 GPU 显存容量计算出可分配的物理块总数。这个计算需要考虑多个因素:模型权重占用的显存、KV 缓存的理论最大需求、以及一定的显存裕量用于应对峰值压力。实际分配时,物理块采用预分配(Pre-Allocation)策略,即在推理服务启动时一次性创建所有物理块并加入空闲列表,避免在推理过程中频繁调用 CUDA 内存分配 API 带来的性能开销。

Block Manager 还负责监控物理块的健康状态。当检测到某块数据因硬件错误或显存位翻转而损坏时,系统会自动将该块标记为无效并触发重新分配。这种容错机制对于长时间运行的推理服务至关重要,能够有效防止因显存故障导致的推理结果异常。

在多租户场景下,物理块管理器需要处理不同请求之间的显存隔离问题。vLLM 通过为每个序列分配独立的逻辑块表来实现租户隔离,而物理块层面的共享仅限于被明确标识为可复用的前缀块。这种设计既保证了前缀缓存的效率,又防止了不同请求之间的数据泄露风险。

与哈希索引层的差异化定位

理解物理块管理器与哈希索引层的关系,对于完整掌握 vLLM 前缀缓存机制至关重要。哈希索引层位于更上层,负责快速判断某个前缀是否已经存在于缓存中,其核心数据结构是跳表(Skip List)或哈希表,用于根据内容哈希值定位到对应的物理块列表。物理块管理器位于更底层,专注于这些物理块在显存中的实际布局、分配与回收。

两层的交互遵循以下流程:當一个新的推理请求到达时,哈希索引层首先查询请求前缀的哈希值,如果命中则返回物理块列表;随后物理块管理器验证这些物理块的有效性(引用计数、访问权限等),并将它们挂载到新序列的逻辑块表中。如果哈希索引未命中,则物理块管理器负责分配新块来存储新生成的内容,并在内容生成完成后将新块的信息注册到哈希索引层,供后续请求使用。

这种分层设计实现了关注点分离:哈希索引层优化查询速度,物理块管理器优化显存利用率。两者独立演进,既保证了前缀定位的低延迟,又确保了复杂显存分配场景下的正确性。

工程落地的关键参数

在实际部署中,有几个关键参数影响物理块管理器的表现。首先是物理块大小,默认值为 16 个 Token,增大块大小可以减少块表开销但会降低缓存粒度,减小块大小则相反。对于前缀复用率高的场景,建议将块大小设置为 32 或 64 以减少内部碎片;对于前缀变化频繁的场景,保持默认的 16 能够提供更精细的缓存复用。

其次是最大物理块数量,这个参数直接影响前缀缓存的容量上限。计算公式为:最大块数 =(GPU 显存总量 - 模型权重 - 运行时开销)÷ 单块大小。建议在预留 20% 显存裕量的基础上设置此参数,确保在高负载下仍有足够的空间进行块分配与淘汰。

最后是淘汰策略的选择。默认的 LRU 策略适用于大多数场景,但如果观察到缓存命中率持续低迷,可以考虑切换到基于访问频率的 LFU(Least Frequently Used)策略,或者使用混合策略结合访问时间与频率进行综合评分。

资料来源

本文技术细节参考 vLLM 官方源码及文档。