逐层 KV 传输#

在逐层粒度上存储和加载 KV Cache 是一个关键优化,它允许前向传播在计算过程中“错开”,因为每一层的 KV Cache 在接收后就可以开始,而不必等到整个加载完成后才开始。

CacheBlend 是在逐层代码路径之上实现的,目的是将重计算和加载进行流水线处理,以掩盖加载 KV Cache 的延迟。

基本代码路径
Click to open full-size

架构概述#

CacheEngine

主要协调器,包含两个主要生成器:

  • 检索生成器 (N + 2 生成): 处理逐层 KV Cache 加载,按需分配内存

  • 存储生成器 (N + 1 yields): 管理逐层 KV Cache 保存,并提前分配 CPU 内存

LayerwiseGPUConnector

管理使用专用 CUDA 流的 GPU-CPU 内存传输:

  • 加载 GPU 缓冲区:用于 CPU→GPU 传输的临时显存(use_gpu: true

  • 存储 GPU 缓冲区:用于 GPU→CPU 传输的临时显存(use_gpu: true

  • 嵌套生成器: batched_to_gpu()batched_from_gpu() 处理实际的内存操作

存储管理器

处理持久存储操作:

  • layerwise_batched_get(): 使用 .result() 进行请求级并发的异步检索

  • batched_put(): 将内存对象存储到持久后端

执行流程#

逐层管道遵循编号的执行顺序:

1. start_load_kv()
  • 通过 lmcache_engine.retrieve_layer() 初始化检索生成器

  • 执行设置(第一次 next())并加载第 0 层(第二次 next()

  • 创建 layerwise_retrievers 列表以进行持续的层处理

2. wait_for_layer_load() (对每一层重复执行)
  • 通过 next() 使检索生成器向前推进以处理第 i 层

  • 触发 StorageManager.layerwise_batched_get() 进行异步缓存查找

  • 调用 GPU Load Generator 的 batched_to_gpu() 将内存对象传输到 GPU

  • 批处理中的最后请求:同步 current_stream.wait_stream(load_stream)

**3. save_kv_layer()**(对每一层重复执行)
  • 首次调用仅:创建具有预先分配 CPU 内存的存储生成器

  • 通过 next() 进展存储生成器以处理第 i 层

  • 调用 GPU 存储生成器的 batched_from_gpu() 将 GPU 数据传输到 CPU

  • 批处理中的第一个请求:同步 store_stream.wait_stream(current_stream)

4. wait_for_save()
  • 通过最后一次 next() 调用来完成存储生成器

  • 完成所有 StorageManager.batched_put() 操作

  • 执行 GPU 存储生成器清理

关键优化#

流水线内存操作

系统将第 N+1 层的计算与第 N 层的存储重叠。

流同步

三个 CUDA 流协调操作:

  • current_stream: vLLM 的前向传播计算

  • load_stream: KV Cache 加载操作

  • store_stream: KV Cache 存储操作

批量级协调

多个请求一起处理,并使用专门的同步机制:

  • 第一次请求:提供存储流同步以防止 GPU 缓冲区损坏

  • 最后请求:提供加载流同步以确保 KV Cache 可用性

内存分配策略
  • 检索:逐层分配

  • 存储:为所有层的预先分配

缓存键管理

多层缓存引擎密钥使用 split_layers(N) 来创建每层的 kubernetes_deployment

配置#

通过设置启用逐层缓存:

use_layerwise: true

系统会根据配置自动选择适当的逐层显卡连接器:

  • VLLMPagedMemLayerwiseGPUConnector: 用于标准逐层操作

  • VLLMBufferLayerwiseGPUConnector: 当启用混合时