尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

KV Cache池化与PD分离:万亿Token级大模型推理优化实践

发布时间:2026/9/24 21:08:22

资讯中心
01
ARTICLE

KV Cache池化与PD分离:万亿Token级大模型推理优化实践

KV Cache池化与PD分离:万亿Token级大模型推理优化实践
先说一个我自己的感受做AI推理服务最让人肉疼的就是Token产出速度跟不上业务想象。前几年聊AI落地大家比的是模型精度现在模型能力普遍够用了大家比的变成了谁能让Token更便宜、更“管够”。很多人把功夫花在量化、剪枝、换小模型上却忽略了另一个巨大的成本黑洞——KV Cache键值缓存。Mooncake这个项目之所以在业内引起关注核心就是它把KV Cache从一个“每块卡各自为政”的显存负担变成了一个可以全局调度、跨机共享的资源池。这个思路听起来简单真做起来牵扯到调度、网络、故障恢复一大堆事。今天我就结合我自己跑推理服务的经验把Mooncake这套“以Token为中心”的架构拆开聊聊尤其是日均万亿Token这种规模下到底会遇到哪些墙、怎么翻过去。先说清楚这篇文章是给谁看的如果你正在做大模型服务的性能优化或者想搞清楚KV Cache池化、Prefill/Decode分离这类技术到底解决什么问题这篇内容可以帮你把来龙去脉理清楚如果你是刚接触推理加速的初学者前半部分的基础拆解也不会让你掉队。我会把关键的计算逻辑、调度策略和踩坑点都讲明白顺便聊聊国产算力环境下类似方案怎么落地。1. 内容整体设计与思路拆解1.1 亿元Token生产究竟难在哪先算一笔账日均万亿Token平摊到一天86400秒大概折合每秒115万Token的产出速度。注意这是“产出”不是“推理一次”。大模型生成Token是一个字一个字蹦的每个Token都要让整个模型前向计算一遍同时把中间计算结果写进显存。如果跑的是7B模型一个Token生成可能只需要几毫秒到几十毫秒但如果是70B甚至更大单卡根本放不下还得走张量并行、流水线并行延迟和显存占用都会指数级上升。所以万亿Token/日不是一个模型推理能搞定的它需要成百上千张GPU同时工作。这里马上就会遇到几个问题第一显存不够。每路会话的KV Cache会随上下文长度线性增长长对话到几千token时KV Cache占用甚至比模型权重还大。第二GPU利用率不均衡。用户发请求有高峰有低谷可KV Cache是按会话独占的不能像CPU内存那样灵活腾挪。第三算力和缓存绑死在同一个设备上一个请求从Prefill阶段进入Decode阶段后算力需求骤降但显存还占着。Mooncake最开始被人提到就是因为它正视了这三点Token生产链路里算力只是让Token生成变快但KV Cache才是让Token“存得下”的瓶颈。所以它的整体设计目标不是单纯优化单个算子而是把整个服务做成“缓存为中心”的架构。1.2 为什么拿KV Cache当突破口先给不熟悉的朋友补一个背景Transformer模型解码时对于每个新Token都需要跟之前的Token做注意力计算。为了避免每步都重新计算之前的Key和Value框架会把历史Token的K、V向量缓存在显存里这就是KV Cache。它可以理解成“这个对话的中间草稿纸”没有它长对话根本没法跑。传统做法里KV Cache跟着请求走一个请求落到某张卡这张卡就给它划一块显存请求结束释放。这样做实现简单但缺陷很明显——同一个文档或同一段系统提示词几百个用户都在问每个人的请求都各自算一遍算出来的KV Cache一模一样却要存N份。Mooncake的突破口就是把“缓存”和“算力”拆开。预填充阶段Prefill也就是处理Prompt的阶段负责把用户的输入文本一次性算成KV Cache解码阶段Decode也就是一个Token一个Token往外蹦的阶段再拿着缓存继续算。这两个阶段对硬件的要求完全不同混在一起跑的时候显卡经常一半时间在算、一半时间在等拆开之后就能各干各的。再加上一层“缓存池”把算好的KV Cache先放进去复用可以让缓存命中率达到一个非常可观的比例Token生产成本自然就降下来了。我自己搭过类似的小型缓存系统最深的一点体会是KV Cache池化不是一个“锦上添花”的优化它是一个架构级别的思维转变。传统推理框架把所有逻辑都看作是“请求驱动”的请求来了才调度而缓存池的思路变成了“数据驱动”先看我这块池子里有没有再决定是直接继续生成还是重新算。这个转变对整个服务的调度器设计影响深远。2. 核心技术拆解PD分离、全局调度与缓存复用2.1 实际拆开看Prefill和Decode为什么要分离很多人第一次听到PD分离Prefill/Decode分离时都会有个疑问这俩本来就是流水线上的两步不在同一块GPU上难道不会增加传输开销吗确实会增加开销但这个开销换来的收益是在“Token产量”维度上几何级的提升。先看Prefill阶段的特点这个阶段要处理的是整个Prompt文本计算量非常大是典型的计算密集型任务适合用高算力的大卡来跑。而且它一次性处理所有输入Token并行度极强基本可以把GPU算力吃得满满当当。再看Decode阶段每次只生成一个Token计算量不大但是延迟敏感YYDS依赖很强而且需要把KV Cache持续保存在显存里。如果让同一块卡既做Prefill又做Decode那同一时刻这张卡的算力很可能只被用到一小部分但显存却被KV Cache占得死死的。一个直观的类比是让一个大货车司机去跑同城快递车厢装不满、油耗却一点没省。Mooncake的做法是让一组卡专门负责Prefill另一组卡专门负责Decode。Prefill集群算完KV Cache之后把缓存放进池子然后通知调度器“这个请求的KV Cache在某个位置”Decode集群再从池子里把对应缓存拉过去继续生成。这样每一块卡都在做自己最擅长的事GPU利用率显著提升。不过不要以为这是免费午餐这里最难的地方在于KV Cache怎么在节点之间高效传输。我这里做个小总结PD分离的收益和代价大概是这样收益一算力利用率提升。Prefill高算力任务集中跑在大卡上Decode延迟敏感任务跑在规模更大的廉价算力上两者互不抢资源。收益二显存压力分散。不再要求每一张卡都能装下最长上下文对话的KV Cache算力池和缓存池可以独立扩容。代价一跨节点KV Cache传输。如果缓存池距离算力池很远网络传输就会吃掉收益这也是为什么这个方案对RDMA网络有硬性要求。代价二调度复杂度上升。原来请求到某一个实例就行现在你要维护“哪块缓存对应哪个会话”、“缓存放在哪个节点”状态管理复杂了一个量级。2.2 “缓存池”到底是什么东西缓存池是Mooncake架构里最核心的抽象。你可以把它理解成内存里的一个全局牌桌所有会话的KV Cache都以键值对的方式存在池子里主键是请求内容的哈希或者语义标识值就是那一大块二进制KV数据。谁需要用到这个缓存就向池子申请一个句柄用完可以还回池子也可以让它自然过期。这个池子并不是只能放在GPU显存里。Mooncake的设计里缓存层的层级其实和计算机的存储体系很像热数据放GPU显存温数据放CPU内存冷数据放NVMe SSD三层联动。为什么需要把KV Cache落到CPU内存甚至磁盘因为GPU显存实在是太贵了而以现在的大模型场景来看大量历史会话的KV Cache根本不需要立刻被访问但如果完全丢弃用户回头问一句“上次那个话题你记得吗”整个重新计算的成本反而更高。缓存的存放位置要和调度器配合。比如一个请求进来调度器先算一下Prompt的哈希去缓存池里查如果命中了一块很长的公共前缀那就只需要从命中位置之后的内容开始做Prefill省掉一大半计算如果完全没命中就直接进Prefill队列算一个全新的KV Cache。注意这里的命中判断并不是纯文本哈希而是要结合Token ID序列来做前缀匹配。实际实现中还需要一个前缀树Trie树来管理共享前缀才能高效判断“新请求和已缓存请求之间最长公共前缀在哪”。我见过很多团队在做类似系统时把缓存池做成了简单的“整包替换”命中率上不去。问题就出在大家都忽略了公共前缀的复用真实业务里系统提示词、长文档、知识库片段这些重复内容才是大头。Mooncake突出的一点就是把这些公共前缀当成一级缓存来管理这让它的缓存命中率能做到远高于朴素方案的水平。2.3 调度器如何决定“Token给谁生产”有了缓存池和PD分离之后接下来一个现实问题就是调度器怎么控制那么多请求到处乱飞。传统推理框架里的调度器通常按队列来管理FCFS或者简单优先级而Mooncake的调度器更像一个实时资源交易系统因为它的决策必须同时考虑算力、缓存、网络带宽三个维度。具体我会拆成三步逻辑第一步请求进来先去缓存池查公共前缀计算“如果从这里续写Tokens能少算多少”这一步是为了决定请求要进Prefill阶段的哪个队列第二步根据Prefill队列结果估算这个请求需要的GPU算力大小和KV Cache的落盘位置第三步决定请求的Decode阶段放在哪个节点执行同时把KV Cache的传输任务排上网。这个调度器厉害的地方在于它会动态调整请求的路由。如果你跑在单机一张卡上根本不需要这种调度但几千张卡的时候每个节点状态都不一样有的节点Prefill算力空闲有的节点显存已经存了很多KV Cache网络链路也有忙闲之分。调度器需要不断收集心跳信息形成全局视图然后像一个“导航系统”一样给每个请求找出最划算的路径。我自己的经验是调度器最怕的是过度设计。刚开始做这类调度时往往加了一堆规则结果性能反而更差。比较稳妥的起步方式是先用两级队列把缓存命中和未命中的请求分开让命中缓存的请求跳过Prefill之后再把Decode负载按显存余量做加权轮询。等这两步稳定了再去考虑更复杂的全局优化策略。Mooncake的调度器在这个方向上走得很远但它的基本盘也是这两板斧。3. 规模化落地万亿Token之后到底是谁在扛3.1 从单机到集群网络和显存如何平衡到了万亿Token/日规模几乎所有在单机场景下忽略的问题都会变成致命的。最典型的就是网络瓶颈。假设你的缓存池远端存放了KV CacheDecode节点每处理一个新Token都需要把完整的KV Cache从远端拉到本地这是相当大的I/O压力。一个20B模型如果上下文长度为8KKV Cache的大小可能在几十MB到几百MB之间如果每秒要处理成百上千个并发请求带宽需求会迅速占满千兆网络甚至万兆网都很紧张。所以Mooncake这类架构在实际落地时基本都是构建在RDMA网络之上的。RDMA最大的特点是能绕过CPU内核直接访问远端内存延迟可以压到微秒级配合GPU Direct技术甚至可以直接把远端数据拉进GPU显存不需要CPU中转。普通TCP在跨机拉取大块数据时CPU中断处理开销非常夸张到一定并发之后CPU占用先爆掉根本轮不到算力瓶颈。这里也给做小规模实验的同行提个醒如果你准备在普通的千兆以太网环境里验证KV Cache池化方案大概率得出“性能反而变差”的结论。不是因为方案不对而是网络栈根本不是为这种高频大块数据传输设计的。至少要在25G或100G网络环境下才能体会到PD分离带来的净收益。再有就是显存和内存之间的平衡。GPU显存虽然快但容量有限CPU内存带宽不如显存但容量大、便宜。Mooncake的思路是尽可能把热数据留在显存冷数据换出去。这个换入换出策略像极了操作系统的页面置换但如果策略太激进频繁换页会让Token生成延迟剧烈抖动太保守显存又会不足。我自己在测试中发现一个实用规则如果KV Cache的复用间隔在30秒以内留在GPU显存收益最大如果超过5分钟就得认真考虑释放给其他请求在中间地带的缓存放CPU内存即可。这个阈值不是固定的但可以作为一个初始参数在线上调优。3.2 Zero-batch Decode如何做到每张卡时刻都在干活再看一个提高吞吐量的技巧叫Zero-batch Decode。传统批处理解码里一个batch里的“活动序列”会随着时间推移逐渐结束有的生成了10个token就停了有的生成了500个token还没完。如果不处理GPU上的垃圾序列就会占着KVCache不算数还拖着其他序列的后腿。动态batching就是解决这个问题的但很多框架实现得比较粗略。Mooncake在这一点上的做法我理解是通过调度器的全局视图让Decode节点上的“批次”始终处于接近满员的状态。它不存在“当前批次做完了再等下个批次”的空窗期因为调度器会持续把新的请求息发到刚空出槽位的节点。用行业术语就是零废弃批处理加上池化KV Cache的即时接入让每个Decode节点几乎永远在计算。从工程实现上这意味着每个Decode节点要能和调度器保持非常高频的状态同步当前有哪些槽位空着、预留给哪些即将结束的请求、KV Cache的传输是否能抢在新的计算任务前完成。这种高频同步如果做得太重开销量会盖过收益所以很多实现都是采用异步事件驱动模式而不是同步轮询。我自己的体会是Zero-batch Decode离普通开发者其实并不远——即使不做PD分离在单一节点上用vLLM的continuous batching也能体会到相似效果。但vLLM的batching是在单节点内跨节点就无能为力了。Mooncake真正想干的事是把这种“绝不空转”的思路推广到整个集群。3.3 面向超长上下文显存吃紧时怎么用池化“续命”接下来专门聊聊长上下文。现在大家玩大模型动不动上下文拉到128K、256K甚至1M这在传统推理架构里简直是灾难。以32B模型、128K上下文为例单请求KV Cache可能占用几个GB的显存。同一张卡上有时候只跑几个长上下文请求显存就爆了。如果没有池化后续请求全部排队系统吞吐直接断崖式下跌。池化在这里的作用有二。第一长上下文往往包含大段公共内容比如一个很长的GitHub仓库代码、一份完整财报多个用户问不同位置的问题时这段公共KV Cache可以被多人复用。第二即使不复用池化层也可以把KV Cache溢出到CPU内存。Decode阶段虽然需要访问全量KV Cache但如果GPU显存不够可以把相对久远的Token的KV Cache放远端需要时再拉回来。这增加了延迟但总比完全跑不起来强。我自己试过给一个16K上下文的业务模型加了一个简单的CPU Offload策略把最近1000个Token的KV Cache留在显存更早的全部放到CPU内存。结果显示长对话场景下吞吐提升了大概40%左右代价是单Token延迟略有上升。如果你也在做长上下文服务可以先试试这个“滑动窗口双向缓存”的思路不必一上来就搭完整的池化平台。4. 趋境AI场景下的适配与思考4.1 从论文到产品还有多少工程差距严格来说Mooncake是一个研究性的架构方案论文发布出来之后很多团队尝试复现但真正能跑到生产水平的不多。为什么因为论文里可以假设网络是无损的、调度器是完美的但生产环境里节点会挂、网络会抖动、NVLink/RDMA可能有兼容问题、显存可能出现碎片化。趋境AI这类技术社区或者团队在讨论“Mooncake如何支撑万亿Token”时其实背后反映的是一类更现实的诉求在不能几十亿买定制硬件的情况下能不能用现有资源池组合出一个“低配版Mooncake”我的理解是可以的但必须学会做减法。减法的第一步就是把“全局缓存池”简化成“本地共享缓存层”。比如用Redis或者内存数据库来做KV Cache的索引管理用GPU节点自带的NVMe盘来做数据分层先用TCP而不是RDMA,把逻辑打通。虽然在极端并发下不如原生方案但对大部分业务来说已经比传统的无缓存方案强太多了。这里面最让我有感触的是KV Cache池化本质上是一个“存储问题”而不是“算力问题”。这就意味着存储领域很多成熟的经验缓存淘汰、分片、镜像、快照都可以迁移过来用。很多做AI服务的团队反而因为不熟悉存储架构一直在用手工管理KV Cache,遇到显存瓶颈就想买卡而不是想怎么把缓存管好。4.2 存量算力上的实践DCU等设备的适配心得顺便说说国产加速卡适配这些新架构时的感受。你也知道现在很多团队手里不只有NVIDIA的卡可能还有海光DCU、华为昇腾这类设备。Mooncake这类方案在设计时主要是基于NVIDIA的GPU特性比如NVLink、GPUDirect RDMA迁移到DCU上会有些水土不服。我自己折腾过一段时间的DCU跑注意力模型最大的感受是它的生态栈对PyTorch的支持已经比前几年好很多但在“细粒度显存管理”和“P2P传输”这些底层能力上还是有不少坑。具体来说Mooncake的池化架构非常依赖“把远端缓存直接映射进显存地址空间”这类能力。如果框架或驱动不支持就只能退而求其次先拷到CPU内存再拷到显存多一次拷贝延迟和带宽都会吃紧。如果你也在做类似适配我的建议是先从缓存索引和调度逻辑入手这部分是纯软件跨硬件相对容易而KV Cache的远端直接访问就得花时间跟硬件的驱动和通信库死磕。可以先拿小规模集群跑通再逐步扩大切忌一上来就按论文配置做大规模验证。4.3 面向业务侧的思考Token越便宜入口越宽阔最后说点更宏观的。为什么现在“Token价格”成了全行业都很敏感的话题因为Token是AI应用的计量单位Token成本高开发者和用户就会收着用Token成本降到足够低大量过去不划算的场景比如整本小说阅读理解、全年财报问答、实时语音对话就会变成新的产品。万亿Token生产能力的意义不只是技术标杆更是商业化想象力。Mooncake这类架构把Token成本打下来的路径和当初云计算把IT成本打下来的路径很像从独占实例变成资源池化从资源独享变成按需使用。云化是Token生产规模化的必经之路缓存的池化、算力的池化、模型的池化最后都会走向一套成熟的Token调度体系。对于做应用的人来说短期不太需要关心底层是Mooncake还是其他方案但一定要关注你的供应商能不能提供“缓存复用”能力。同样一个文档问答产品有缓存复用的厂商和没缓存复用的厂商背后成本可能差3到5倍。这个差距迟早会反映到报价上。5. 常见问题与排查技巧实录5.1 缓存命中率低先查前缀规范化这是做缓存池之后最先遇到的问题。明明我感觉很多请求都带同一段系统提示词怎么缓存命中率就是上不去后来查下来问题出在请求没有做规范化处理。比如同样的知识库内容有些请求带了换行符有些带了不同的System Prompt前缀导致前缀树的匹配路径变了。排查思路不复杂先在调度日志里拉出所有未命中缓存的请求对比它们的前缀Token序列。如果发现“前几十个Token都不同但内容其实一样”基本就是规范化没做好。解决方式包括对Prompt做格式化统一换行、去掉多余空格、把固定System Prompt单独抽取出来统一拼接、对文档块做语义或哈希级别的预分块。还有一个容易被忽视的点缓存的Key不能直接用字符串要算Token ID序列。因为同一个字符串可能被切分成不同的Token序列比如加了特殊token、BOS等字符串哈希能对上也未必能命中Token级别的缓存。最好的做法是记录模型实际收到的Token ID前缀用这个前缀计算语义键。5.2 跨节点KV Cache传输太慢网络栈背锅现象是缓存命中率上去了Decode速度反而下降了。查了一圈问题出在KV Cache拉取的耗时超过了缓存复用省下的计算时间。这种情况大部分是网络栈的选择问题。普通TCP传输大块KV Cache时数据要经过“远端GPU显存→远端CPU内存→远端网卡→本地网卡→本地CPU内存→本地GPU显存”这条链路过好几趟每一趟都有拷贝和中断开销。排查方法是看网络监控里的吞吐量和时延。如果吞吐量远低于网卡理论值先检查是不是走了CPU拷贝路径有没有启用RDMA或GPUDirect。如果在没有RDMA的环境里做验证要做好心理准备跨节点缓存拉取的开销可能非常大这时宁可让Prefill节点直接把新的KV Cache传给Decode节点也不要走“先落地再拉取”的路径。如果网络确实上不去还可以尝试“预取”。调度器在提示词阶段就算出这个请求大概率会继续生成很多Token那就提前把KV Cache拉到Decode节点显存里等着而不是等Decode真正需要时才发起传输。根据我的经验一个简单的预取队列就能让平均等待时间下降30%以上。5.3 显存碎片和OOM静态缓存和动态流量的矛盾再做细一点还会遇到显存碎片。KV Cache池化以后显存里会同时存在长期缓存块和短期请求块。长期缓存块是陆续释放、陆续分配的时间一长显存会碎得像奶酪新来的大请求找不到连续空间直接OOM跑崩。这类问题最好用一套显存管理库来解决市面上有专门做“显存池化”的库原理和内存池类似。如果暂时不想依赖外部库一个实用技巧是对长缓存块做“整页分配”也就是给缓存块分配时都按照固定对齐单元这样即使内容释放空间也能按页回收并重组。同时在业务上给超长请求设置超时保护避免某个请求占着超级大块显存不放。还有一个很少有文档提到的坑动态Batch的调度器如果启用了“显存预留”功能最好预留量不是固定值。因为长上下文请求的KV Cache增长曲线是线性的预留太大会浪费预留太小会在生成到一半时OOM。可以按请求的历史平均长度做一个自适应预留每批次更新一次能显著降低OOM次数。5.4 调度器要不要单独拆服务别过度拆很多团队在落地时会考虑把调度器拆成一个独立的中心化服务统一管理所有推理节点。这个方向没问题但要注意中心化调度器本身会成为单点和性能瓶颈。调度器要跟每个节点同步状态、做路由决策如果请求量非常大调度器自己的CPU和网络就成了瓶颈拖垮整个系统。比较务实的做法是分层本地每个机架或每个机房有一个小调度器负责本区域内的资源分配上层再有一个全局调度器只处理跨区域负载均衡和全局缓存索引。这个架构比单中心调度器复杂但扩展性好很多。如果团队规模不大建议先用一个全局调度器做MVP但一定要想好它的状态存储用什么。KV Cache索引如果放到单机Redis上流量大了会很被动建议直接上分布式KV存储。说到底技术方案的演进是一个不断“往复杂度走又不断简化”的过程。从Mooncake的论文到生产落地中间隔着的不是想法而是无数个像这样的工程细节。我个人的体会是做Token生产优化最怕只盯着算力指标不放真正的红利往往在你意想不到的角落——缓存命中率、前缀规范化、显存碎片率、网络传输路径每一个点都能省出相当可观的成本。如果能手头资源有限建议先抓好缓存复用和网络链路这两件事它们带来的收益是最直观的。最后再分享一个小技巧给缓存池加上观测埋点实时看“命中率、缓存大小、淘汰次数、跨节点传输量”四个指标。很多团队做完缓存池之后因为基本没有监控连缓存失效了都不知道。有了这些指标你就能清楚地知道优化到底有没有效果。我见过太多上线即盲跑的案例输勤勤恳恳做了个KV Cache池化最后效果不升反降排了一天查不出来。把这些指标盯住至少能少走一半弯路。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。