vLLM 与 SGLang KV Cache 底层实现机制深度调研报告

基于 vLLM(vllm-project/vllm)与 SGLang(sgl-project/sglang)main 分支源码,2026-08-21。


一、底层 KV Cache 实现机制(基于 main 分支源码)

1. vLLM:PagedAttention + 哈希链前缀缓存

分层结构KVCacheManager (vllm/v1/core/kv_cache_manager.py) → KVCacheCoordinator (kv_cache_coordinator.py,按 KV 类型分组:全注意力/滑窗/Mamba) → 每组一个 SingleTypeKVCacheManager → 共享一个 BlockPool (vllm/v1/core/block_pool.py)。

核心数据结构(直接读源码验证):

  • BlockPool.blocks: 全量 KVCacheBlock 数组;free_block_queue: 空闲块双向链表(同时充当 LRU 驱逐队列------被缓存但空闲的块留在队尾作驱逐候选)。
  • BlockHashToBlockMap: dict[BlockHashWithGroupId, KVCacheBlock | dict[int, KVCacheBlock]],hash→物理块索引。同 hash 多块时退化为 dict(源码注释明确说明不做去重,以保证 block table append-only)。
  • 块哈希是链式的 :第 i 块的 hash = f(父块 hash, 本块 16 个 token id, extra_keys),见 kv_cache_utils.pyhash_block_tokens/resolve_block_hashes。这保证前缀任何位置不同则后续全部 miss。
  • 命中流程:请求创建时预计算全序列 block_hashes → get_computed_blocks() 逐 hash 查 cached_block_hash_to_block → 命中块 ref_cnt++(touch),只 prefill 未命中尾部;新满块经 cache_full_blocks() 注册进哈希索引。

物理层gpu_model_runner._allocate_kv_cache_tensors()torch.zeros(int8) 分配一整块 buffer(按 block_stride 打包多层别名同一 backing),再 reshape 成各 attention backend 的 KV shape;kernel 通过 slot_mapping 散射写入、block_table 分页寻址(vllm/v1/attention/backends/flash_attn.py)。

调度与抢占 :V1 抢占只有 recompute 模式 (无 CPU swap)------Scheduler._preempt_request() 直接把 num_computed_tokens 清零、释放块、重新排队重算。长 prompt 被抢占 = 全部 prefill 重来,这是 agent 场景最贵的失败模式。

2. SGLang:RadixAttention 前缀树 + 两级内存池

Radix Treepython/sglang/srt/mem_cache/radix_cache.py,已验证):

  • TreeNode: children: defaultdictkey: RadixKey(token 序列 + extra_key LoRA 隔离 + cache_salt)、value(KV 物理页索引 tensor)、lock_ref 引用计数、last_access_timehost_value(host 层副本)。
  • match_prefix(): 从 root 下行匹配;RadixKey.match()指数搜索+二分 找首个分叉 token(避免逐 token Python 循环);命中落在节点中间时 _split_node 切分;结果按 page_size 对齐截断。
  • evict(): 只驱逐叶子(evictable_leaves 堆),默认按 last_access_time LRU(策略工厂支持 lru/lfu/fifo/slru/priority);父节点变叶子后入堆继续驱逐;lock_ref > 0 的节点(被 in-flight 请求引用)受保护。
  • 与 vLLM 的本质区别:vLLM 是「定长块 + 平铺哈希表」,SGLang 是「变长节点的前缀树」------树天然表达前缀包含关系,且支持 token 级 page_size=1 的细粒度复用。

物理池mem_cache/memory_pool.py + allocator/):

  • ReqToTokenPool: 请求→token 物理位置的页表;MHATokenToKVPool/MLATokenToKVPool 等:真正的 KV tensor(NHD/page-major 布局;MLA 存压缩 latent)。
  • PagedTokenToKVPoolAllocator(page_size>1 时):alloc/free 用 CUDA kernel 批量操作 free page 列表。

HiRadixCache 层次缓存hiradix_cache.py):在 GPU (L1) 之上叠 host 内存 (L2) 和外部存储 (L3),write_through/write_back 写策略 + prefetch;L3 后端工厂(storage/backend_factory.py)注册了 mooncake / hf3fs / nixl / aibrix / file / shm 等------即 KV 可以落到跨节点分布式存储。

graph TB subgraph vLLM V1 A[KVCacheManager] --> B[KVCacheCoordinator<br/>full-attn / SWA / Mamba 分组] B --> C[BlockPool<br/>KVCacheBlock[] + free 双向链表 LRU] C --> D[BlockHashToBlockMap<br/>链式哈希 → 物理块] C --> E[int8 大buffer reshape<br/>slot_mapping + block_table] end subgraph SGLang F[RadixCache / HiRadixCache] --> G[RadixTree<br/>TreeNode + lock_ref] G --> H[ReqToTokenPool 页表] G --> I[MHA/MLA TokenToKVPool<br/>+ PagedAllocator] F -.L2/L3.-> J[Host mem / Mooncake / 3FS / NIXL] end

二、问题一:agent 长时程任务下如何高吞吐

按「省显存 → 提复用 → 架构级扩展」三档,均为两引擎已落地的机制:

手段 原理 vLLM 落地 SGLang 落地
Prefix caching 复用共享前缀跳过重复 prefill 默认开启,链式哈希 Radix 树,勿 --disable-radix-cache
KV 量化 fp8/nvfp4 直接减半/减 75% 占用 --kv-cache-dtype fp8(Blackwell 可 nvfp4) 同名参数,MLA 有独立 fp8 路径
Chunked prefill 长 prompt 切块与 decode 混排,削激活峰值 默认开,max_num_batched_tokens --chunked-prefill-size
SWA 稀疏缓存 滑窗层只留窗口内 KV,窗外即时释放 hybrid cache manager 区分 full/SWA 组 SWAChunkCache + free_swa_out_of_window_slots
KV offloading 冷 KV 异步搬到 CPU/SSD/远端 OffloadingConnector(CPU 单层 / Tiering 多级 FS/OBJ/P2P,lru/arc 策略) HiRadixCache(--hicache-size + L3 backend)
PD 分离 prefill/decode 各自独立 GPU 池,RDMA 传 KV MooncakeConnector / NixlConnector(Pull/Push) --disaggregation-mode + mooncake/nixl 后端
准入控制 watermark 预留 + max_num_seqs + 抢占兜底 watermark_preempt_request(recompute) retract_decode

agent 场景特有的三个杠杆(往往比引擎调参收益更大):

  1. Prompt 结构设计 :把稳定的 system prompt、工具 schema 放在最前面,每轮变化的工具结果放尾部------前缀哈希/radix 匹配要求逐字节一致,前缀处任何一个时间戳都会让命中点后移。
  2. Session 粘性路由:同一会话路由到同一实例,最大化本地缓存命中(配合下方问题二)。
  3. Context 压缩:LLMLingua 类工具在 prefill 前裁剪 prompt;LMCache 的 CacheBlend 支持非前缀位置 KV 复用+局部重算。

组合原则:先提命中率(前缀缓存+路由+prompt 结构)省 prefill → 再量化省单位占用 → 再 offloading 省总量 → 最后 PD/L3 做横向扩展。业界参照:Mooncake(Kimi,arXiv:2407.00079,KVCache-centric 分离架构)、LMCache(arXiv:2510.09665)、NVIDIA Dynamo+NIXL。


三、问题二:KV Cache 是否只能存本机、必须粘性路由?

这个说法一半过时、一半仍是工程现实。

过时的部分------跨节点分布式 KV 已经是两个引擎的一等能力:

路径 代表实现 成熟度
远端 KV 池(L3) SGLang HiCache + Mooncake Store/3FS/NIXL/AIBrix 后端;vLLM OffloadingConnector Tiering(FS/OBJ/P2P)、LMCache(CPU/SSD/Redis/Mooncake) 生产可用,多实例共享同一份前缀 KV,无需粘性路由也能命中
PD 间 KV 直传 vLLM MooncakeConnector/NixlPushConnector(RDMA 直写对端 GPU 显存);SGLang disaggregation/(bootstrap handshake + mooncake/nixl conn) 大规模生产验证(Mooncake 论文即 Kimi 生产架构)
KV 事件发布 vLLM kv_events.py(BlockStored/BlockRemoved,ZMQ pub-sub),供外部 gateway 建全局 KV 索引 较新,生态建设中

另外纠正一个隐含前提:即便单机内,TP 多卡时 KV 本来就是按 head 切分存在多卡上的------"单节点"本身已是分布式存储。

仍然成立的部分 ------粘性路由依然是主流默认,原因是经济性而非技术不可行

  • 跨节点取 KV 的传输成本(即使 RDMA,GB 级 KV 也要毫秒~十毫秒级)vs 就地重算 prefill 的成本,只有命中率足够高时传输才划算;
  • 本地 radix/prefix 缓存命中是零成本、零延迟的;
  • 所以生产栈(NVIDIA Dynamo、vLLM Production Stack、SGLang Router)的标准做法是 cache-aware routing:先按前缀哈希做亲和性调度把命中率做满,分布式 KV 作为溢出层处理"亲和节点已被驱逐/重启"的长尾。

选型建议

  • 单集群、会话可路由 → 粘性/cache-aware 路由 + 本地缓存即可,不必上分布式 KV;
  • 多实例需要共享大 system prompt/RAG 语料、或实例弹性伸缩导致亲和失效频繁 → 上 LMCache 或 SGLang HiCache L3(Mooncake 后端);
  • 已有 RDMA 网络、追求极致吞吐 → PD 分离 + NIXL/Mooncake 直传。

来源 :vLLM 源码 vllm/v1/core/{block_pool,kv_cache_manager,kv_cache_utils}.pyvllm/distributed/kv_transfer/kv_connector/v1/docs/features/{mooncake_connector_usage.md,kv_offloading_usage.md};SGLang 源码 python/sglang/srt/mem_cache/{radix_cache,hiradix_cache,memory_pool}.pypython/sglang/srt/disaggregation/docs/docs/advanced_features/{hicache_design,pd_disaggregation}.mdx;业界资料 Mooncake arXiv:2407.00079LMCache arXiv:2510.09665kvcache-ai/MooncakeLMCacheNVIDIA Dynamo 文档

相关推荐
考虑考虑1 小时前
Java实现hmacsha256加密算法
java·后端·java ee
Bode_20021 小时前
多资源规划(含生产调度、库存管理及产能规划)的创新优化算法
算法·调度
凤山老林2 小时前
聊聊企业级 API 安全:Spring Boot 落地 OAuth 2.1、mTLS 与接口签名防篡改
spring boot·后端·安全·oauth
土司大王2 小时前
LeetCode hot100——35.搜索插入位置:Java 二分模板、左闭右开区间与插入点分析
java·算法·leetcode
微三云生态系统架构师-彭丹2 小时前
远方好物S2B2C系统架构:一级分销与保证金托管的合规技术实现
人工智能·算法
土司大王2 小时前
LeetCode hot100——34.在排序数组中查找元素的第一个和最后一个位置:Java 二分模板、边界分析
java·算法·leetcode
林鹿3 小时前
maven属性与groovy变量对照表
后端
烽火戏诸诸诸侯3 小时前
AI 让我的独门小工具焕发第二春
后端·ai编程·vibecoding
狼爷4 小时前
硅谷爆火的FDE是AI新风口,还是高级外包?
后端·全栈