KV Cache CPU 卸载指南¶
概述¶
KV Cache CPU 卸载功能可将不活跃的 KV cache 块从 NPU 内存卸载到 CPU 内存,从而在 NPU 内存有限时,让 vLLM 能够处理更长的上下文或更多的并发请求。当 NPU 上发生前缀缓存未命中,但数据存在于 CPU 内存中时,KV cache 会被异步加载回 NPU,从而降低重计算延迟。
该功能基于 vLLM 的 OffloadingConnector 框架构建。上游的 CPUOffloadingSpec 和 TieringOffloadingSpec 名称已注册到 Ascend NPU 实现中,这些实现使用专用 NPU 流在 NPU 与 CPU 之间进行高效的异步数据传输。
核心概念¶
- CPU 块池:预先分配的 CPU 内存块池。仅使用 CPU 的 spec 在可用时使用固定(pinned)张量;多层级 spec 使用共享的 mmap 区域,以便调度器和二级层级可以访问相同的字节。
- 异步传输:NPU 到 CPU(D2H)和 CPU 到 NPU(H2D)传输在独立的 NPU 流上执行,与计算重叠,以最大程度减少延迟影响。
- LRU 淘汰:CPU 侧块池使用 LRU(最近最少使用)淘汰策略来高效管理有限的 CPU 内存。
- 多层级卸载:
TieringOffloadingSpec复用 vLLM 的分层管理器,支持 CPU 主层级,并可选配二级层级,例如文件系统存储。
使用方法¶
Python API¶
from vllm import LLM, SamplingParams
from vllm.config import KVTransferConfig
kv_transfer_config = KVTransferConfig(
kv_connector="OffloadingConnector",
kv_role="kv_both",
kv_connector_extra_config={
"cpu_bytes_to_use": 8 * (1 << 30),
"blocks_per_chunk": 8,
"spec_name": "CPUOffloadingSpec",
},
)
llm = LLM(
model="Qwen/Qwen3-0.6B",
gpu_memory_utilization=0.5,
kv_transfer_config=kv_transfer_config,
)
sampling_params = SamplingParams(max_tokens=100, temperature=0.0)
outputs = llm.generate(["Hello, my name is"], sampling_params)
for output in outputs:
print(f"Prompt: {output.prompt!r}")
print(f"Generated: {output.outputs[0].text!r}")
在线服务¶
vllm serve Qwen/Qwen3-0.6B \
--gpu-memory-utilization 0.5 \
--kv-transfer-config '{
"kv_connector": "OffloadingConnector",
"kv_role": "kv_both",
"kv_connector_extra_config": {
"cpu_bytes_to_use": 8589934592,
"blocks_per_chunk": 8,
"spec_name": "CPUOffloadingSpec"
}
}'
多层级卸载¶
kv_transfer_config = KVTransferConfig(
kv_connector="OffloadingConnector",
kv_role="kv_both",
kv_connector_extra_config={
"cpu_bytes_to_use": 8 * (1 << 30),
"blocks_per_chunk": 8,
"spec_name": "TieringOffloadingSpec",
"secondary_tiers": [
{
"type": "fs",
"root_dir": "/tmp/vllm_kv_offload",
}
],
},
)
配置参数¶
kv_connector:必须设置为"OffloadingConnector"。kv_role:设置为"kv_both"以同时启用 KV cache 的存储和加载。cpu_bytes_to_use:所有 worker 为 CPU 层级预留的总字节数。这是 vLLM 卸载 spec 所必需的。blocks_per_chunk:每个被卸载的 chunk 中包含的 NPU KV-cache 块数量。该值必须大于零。另外,当所有 KV-cache 组使用相同 block size 时,block_size可以指定以 token 为单位的 chunk 大小;这两个选项互斥。spec_name:使用上游名称"CPUOffloadingSpec"进行仅 CPU 卸载,或使用"TieringOffloadingSpec"进行多层级卸载。vLLM Ascend 会将它们透明地映射到 NPU 实现。secondary_tiers:TieringOffloadingSpec使用的可选 vLLM 分层后端列表。每个条目是一个 dict;常用键:type:后端类型。使用"fs"表示文件系统存储。root_dir:该层级写入块文件的目录。n_read_threads/n_write_threads:该层级 I/O 的线程池大小。对于高带宽后端(例如基于 RDMA 的 3FS),增加这些值有助于充分利用并行带宽。
调优文件系统(SSD / 3FS)二级层级¶
fs 层级复用 vLLM 的批处理文件系统实现。它在启动时检测 O_DIRECT,当文件系统拒绝直接 I/O 时自动回退到缓冲 I/O。要获得最佳的磁盘/3FS 卸载吞吐量:
- 增大
blocks_per_chunk,以传输更大的 chunk 并降低元数据开销,同时要考虑由此带来的较粗的缓存命中粒度。 - 提高
n_read_threads/n_write_threads以匹配后端的并行度(与本地 SSD 相比,基于 RDMA 的 3FS 可以从更高的并发中获益)。 - 保持 CPU 主层级(
cpu_bytes_to_use)足够大,使得较慢的磁盘层级仅在处理真正的冷数据时才会被访问——只有当工作集超过 CPU 容量时,二级层级才会提升性能。 - 文件系统(SSD / 3FS)二级层级的 kvcache 淘汰策略仍在开发中,将在未来版本中提供支持。
工作原理¶
- 正常推理:KV cache 块照常计算并存储在 NPU 上。
- 卸载到 CPU:当 NPU 内存已满且需要新块时,不活跃的 KV cache 块会通过专用 D2H NPU 流异步复制到 CPU 内存。
- 前缀缓存命中(CPU):当请求与之前计算的数据共享前缀,且 NPU 上未找到前缀缓存但 CPU 内存中存在该数据时,KV cache 块会通过专用 H2D NPU 流从 CPU 异步加载回 NPU。
- LRU 管理:当 CPU 内存已满时,CPU 块池使用 LRU 淘汰策略来丢弃最近最少使用的块。
可选:KV Cache 事件¶
您可以启用 KV cache 事件发布以进行监控或调试:
from vllm.config import KVEventsConfig
kv_events_config = KVEventsConfig(
enable_kv_cache_events=True,
publisher="zmq",
endpoint="tcp://*:5555",
topic="kv_events",
)
llm = LLM(
model="Qwen/Qwen3-0.6B",
gpu_memory_utilization=0.5,
kv_transfer_config=kv_transfer_config,
kv_events_config=kv_events_config,
)
注意事项¶
- 此功能需要 vLLM v1 引擎。
- 根据可用的 CPU 内存调整
cpu_bytes_to_use。保留过多可能会导致宿主机出现内存不足(out-of-memory)错误。 CPUOffloadingSpec在可用时使用固定(页锁定)张量。TieringOffloadingSpec必须使用 vLLM 的共享 mmap 主层级;CANN 没有提供与 CUDA 任意 mmap 主机注册在内存拷贝方面兼容的等效机制,因此其 NPU↔CPU 带宽必须在目标宿主机和对应的 CANN 版本上单独进行基准测试。gpu_memory_utilization参数控制为 KV 缓存保留多少 NPU 内存。较低的值会为 KV 缓存留下更少的 NPU 内存,从而使卸载更加频繁。- 对于生产工作负载,请使用真实的请求模式进行基准测试,以找到最佳的
cpu_bytes_to_use和blocks_per_chunk设置。
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 vl/vllm-ascend