深入浅出 RDMA 编程:高性能网络技术全解析
Mooncake:以 KVCache 为中心的 LLM 推理解耦架构详解
本文基于 Moonshot AI 发表的两篇论文:
- Mooncake: A KVCache-centric Disaggregated Architecture for LLM Serving(arXiv 2407.00079)
- MOONCAKE: Trading More Storage for Less Computation(FAST '25)
Mooncake 是 Kimi 大模型服务的底层推理平台,目前已在数千节点上稳定运行,每天处理超过 1000 亿 tokens。
1. 背景:LLM 推理的核心挑战
1.1 推理的两个阶段
基于 Transformer 的大语言模型(如 GPT、LLaMA)采用 decoder-only 架构,每次推理请求逻辑上分为两个截然不同的阶段:
Prefill 阶段(预填充):并行处理所有输入 token,生成第一个输出 token,同时计算并存储所有层的 Key/Value 中间结果,即 KVCache。由于注意力计算复杂度随序列长度二次增长(),该阶段是计算密集型的。
Decode 阶段(解码):利用已有 KVCache 自回归地逐 token 生成输出,每次迭代仅处理 1 个 token。这使得它是内存密集型的,计算时间随 batch size 亚线性增长。

图 1:不同序列长度和 batch size 下,Prefill(左)和 Decode(右)的归一化吞吐量与延迟。Prefill 时间随输入长度超线性增长,Decode 时间随 batch 亚线性增长。
1.2 服务质量目标(SLO)
作为 MaaS(Model as a Service)提供商,Kimi 需要同时满足两类延迟约束:
- TTFT(Time To First Token):请求到达至第一个 token 输出的时间,主要受 Prefill 阶段影响
- TBT(Time Between Tokens):连续两个 token 之间的生成间隔,主要受 Decode 阶段影响
典型约束形如 ,,即 90% 的请求需满足此限制。优化目标是在满足 SLO 的前提下最大化有效吞吐量(goodput)。
1.3 传统架构的痛点
在传统的耦合式推理系统(如 vLLM)中:
- Prefill 和 Decode 混跑在同一批次中,长上下文的 Prefill 会严重干扰 Decode 阶段的 TBT
- KVCache 只能存在于单节点的 HBM(GPU 显存)或本地 DRAM 中,缓存容量有限,跨会话前缀复用率低
- 无法充分发挥集群中大量闲置的 CPU、DRAM、SSD 和 RDMA 资源
2. 核心设计哲学:以 KVCache 为中心,用存储换计算
Mooncake 的核心洞察是:KVCache 的调度是 LLM 推理调度的核心。
2.1 两大吞吐量提升路径
提升整体吞吐量通常有两条路径:
- 最大化 KVCache 复用:避免冗余计算,降低 TTFT
- 最大化每批次 token 数:提高 MFU(Model FLOPs Utilization),但会增大 TBT
这两条路径都与 SLO 存在潜在冲突。Mooncake 的关键在于精细调度,让二者协同而非对抗。
2.2 "用存储换计算"的数学依据
以 LLaMA3-70B 为例,若当前请求长度为 ,其与已缓存 KVCache 的公共前缀长度为 ,则:
- 节省的计算量 ( 为层数, 为模型维度)
- 需要传输的 KVCache 大小
设 GPU 计算吞吐 ,KVCache 加载带宽 (取 和 的较小值),则当:
时,复用 KVCache 对 TTFT 是净收益的。对于 LLaMA3-70B(8×A800),当前缀长 8192 时,所需最小带宽仅约 6 GB/s。这意味着一块 100 Gbps 的 NIC 就足以支撑分布式 KVCache 复用——这是构建全局缓存池的理论基础。
3. 整体架构

图 2:Mooncake 整体架构。系统将 GPU 集群解耦为三类资源池:Prefill 节点池、Decode 节点池,以及由 CPU、DRAM、SSD、RDMA NIC 组成的分布式 KVCache 存储(Mooncake Store)。
Mooncake 采用以 KVCache 为中心的解耦架构,核心组件包括:
| 组件 | 职责 |
|---|---|
| Conductor(全局调度器) | 感知 KVCache 分布与工作负载,为每个请求选择最优的 Prefill/Decode 实例组合 |
| Prefill 节点池 | 专门执行 Prefill 计算,支持跨节点流水线并行处理超长上下文 |
| Decode 节点池 | 专门执行自回归解码,采用 continuous batching 最大化 batch 利用率 |
| Mooncake Store | 分布式 KVCache 存储系统,汇聚集群所有节点的 CPU DRAM/SSD,通过 RDMA 高速互联 |
| Messenger | 每个节点部署的 KVCache 传输服务,基于 GPUDirect RDMA 实现零拷贝高速传输 |
3.1 请求处理全流程

图 3:一个请求在 Mooncake 中的完整处理流程,涵盖 KVCache 复用、增量 Prefill、异步传输和 Decode 四个步骤。
一个请求到来时,经过如下步骤:
Step 1:KVCache 复用(KVCache Reuse)
Conductor 对输入 token 序列计算分层哈希,在全局 KVCache 索引中查找最长前缀匹配。选定 Prefill 实例后,将命中的 KVCache 块从远端 CPU DRAM 通过 RDMA 加载到该实例的 GPU HBM,直接跳过这部分的重复计算。
Step 2:增量 Prefill(Incremental Prefill)
仅对未命中前缀的 token 执行 Prefill 计算。若未命中 token 数超过阈值(prefill_chunk,通常 >1000 tokens),则将其拆分为多个 chunk,以流水线方式分发到多个 Prefill 节点并行处理(见 §5 的 CPP 机制)。
Step 3:KVCache 流式传输(KVCache Transfer)
Prefill 阶段逐层计算完成后,每层的新增 KVCache 立即异步流式传输到目标 Decode 节点的 CPU DRAM,与计算过程并行,消除等待时间。
Step 4:解码(Decoding)
KVCache 全部就绪后,请求加入 Decode 节点的 continuous batch 队列,开始自回归生成。Decode 节点由 Conductor 根据当前负载预先选定,以保证 TBT SLO。
4. Mooncake Store:分布式 KVCache 存储
Mooncake Store 是 Mooncake 的核心基础设施,它将 GPU 集群中大量闲置的 CPU DRAM、SSD 和高速 RDMA 网络整合为一个统一的全局 KVCache 缓存池。
4.1 KVCache 的分块与哈希管理

图 4:CPU 内存中的 KVCache 池。每个块附带由其内容哈希和前缀哈希共同计算出的 hash key,用于全局去重和匹配。
KVCache 以**分页块(paged blocks)**的形式存储,每块包含固定数量的 token(通常 16~512 tokens)。块的唯一标识 hash key 由以下方式计算:
1 | block_hash[i] = Hash(tokens[i*B : (i+1)*B] || block_hash[i-1]) |
即当前块 token 内容与前缀块哈希拼接后取哈希,确保相同 token 前缀对应相同的 hash key,从而实现跨请求、跨会话的前缀去重复用。
缓存满时采用 LRU 淘汰策略(实验对比中 LRU 在该负载下性能最优),正在被访问的块不会被淘汰。热点块(被大量请求共享的系统 prompt 等)会通过调度策略主动复制到多个节点,降低访问延迟。
4.2 为何需要全局缓存而非本地缓存?
以 LLaMA3-70B 为例,单个 token 的 KVCache 大小约为 320 KB。即便为每个节点分配 1 TB DRAM 作为本地缓存,也只能存储约 300 万个 token。
实验分析表明:在多种真实负载下,本地 3M token 容量的缓存仅能达到理论最大命中率的不到 50%。而将 20+ 个节点的 DRAM 汇聚成全局缓存池(容量约 50M tokens),才能接近理论上限。
全局缓存 vs 本地缓存的实测收益:
- 缓存命中率最高提升 136%
- Prefill GPU 计算时间最高节省 48%
4.3 高性能传输引擎
Mooncake Store 的传输引擎基于 RDMA(Remote Direct Memory Access) 实现,目标是充分利用多 NIC 设备(每台 A800 节点配备 4×200 Gbps 或 8×400 Gbps NIC)。

图 5:Mooncake Store 传输引擎。通过拓扑感知的路径选择,将传输任务分配到最优 NIC,避免 UPI/PCIe 瓶颈,并将单次传输切分为 16 KB 的小片,多路并行传输。
拓扑感知路径选择(Topology-aware Path Selection)
现代服务器内部存在 NUMA 域、PCIe Switch 等拓扑层次,跨域传输会受到 UPI 带宽限制。传输引擎在启动时让每台服务器生成拓扑矩阵并广播到集群,为每种内存类型(CPU DRAM、GPU VRAM)维护"首选 NIC"和"备用 NIC"列表。正常情况下,数据传输优先使用与内存同 NUMA 域的 NIC,实现 RDMA 操作不跨 NUMA;故障时自动切换到备用路径。
每次传输被内部切分为 16 KB 的小片,不同片可以通过不同 NIC 并发传输,充分聚合所有 NIC 的带宽。
端点池(Endpoint Pooling)
连接按需建立(首次请求时才配对),并通过 SIEVE 算法管理端点池,限制活跃连接数防止开销过大。NIC 故障时自动切换到其他路径,连接恢复后重新加入池。
实测效果:在 40 GB 数据传输(对应 LLaMA3-70B 128k token 的 KVCache)场景下,传输引擎在 4×200 Gbps 网络下达到 87 GB/s,在 8×400 Gbps 网络下达到 190 GB/s,分别是 TCP 方案的 2.4× 和 4.6×。
5. Prefill 节点池:处理超长上下文
5.1 为何坚持 Prefill/Decode 分离?
部分研究(如 Sarathi-Serve)认为 Chunked Prefill 可以避免 P/D 分离的复杂性——将 Prefill 分成小块插入 Decode batch,减少干扰。
Mooncake 在权衡后选择坚持分离架构,理由有三:
- SLO 不可兼顾:Chunked Prefill 内嵌到 Decode batch 时,难以同时最大化 Prefill MFU 和满足 TBT SLO——提高 Prefill 吞吐必然增大 Decode 延迟抖动
- 超长上下文需要跨节点并行:128k~1M token 的请求仅靠单节点 8 卡无法高效处理,需要多节点协作
- VRAM 利用机会:分离的 Prefill 节点 VRAM 在 KVCache 完成流出后可自由利用(如处理异步批处理任务)
5.2 分块流水线并行(Chunked Pipeline Parallelism,CPP)
对于超长上下文(如 128k tokens),Mooncake 提出 CPP 机制替代序列并行(SP):
工作原理:
- 将 Prefill 集群中的若干节点组成一个"流水线 Prefill 组"
- 一个请求的输入 tokens 被切分成多个 chunk,每个 chunk 不超过
prefill_chunk阈值 - 不同 chunk 分配给流水线组内的不同节点并行处理,类似训练中的流水线并行
与序列并行(SP)的对比:
| 维度 | 序列并行(SP) | 分块流水线并行(CPP) |
|---|---|---|
| 跨节点通信 | 每层至少一次全局通信(Ring/Striped Attention) | 仅在流水线阶段边界通信,可与计算重叠 |
| MFU | 较低(频繁通信) | 更高 |
| 动态扩缩容 | 复杂,需要全局通信组 | 简单,节点组大小可灵活调整 |
| 短请求处理 | 低效(SP 引入额外开销) | 自然降级为单节点处理,无额外开销 |
CPP 的核心优势在于:跨节点通信仅发生在 chunk 边界,且可以与计算完全重叠,网络资源不与 KVCache 传输竞争。
5.3 逐层 Prefill(Layer-wise Prefill)
为进一步降低 VRAM 占用并减少传输等待,Mooncake 实现了逐层异步 KVCache 加载与存储:
1 | for layer in range(num_layers): |
这种方式使 KVCache 的传输与 GPU 计算完全并行,Prefill 实例的实际延迟约等于 max(KVCache 加载时间, 标准 Prefill 时间)。
对于长上下文请求(KVCache 加载量大),逐层 Prefill 可显著降低 KVCache 存储延迟。此外,由于 KVCache 实时流出,Prefill 调度时可以忽略 VRAM 可用量(只需能容纳单请求即可),极大简化了调度器设计。
6. KVCache 感知调度
Conductor 是 Mooncake 的全局大脑,其调度算法以 KVCache 分布为核心,兼顾负载均衡和 SLO 满足。
6.1 缓存感知的 Prefill 调度
传统系统按请求数量做负载均衡,Mooncake 的 Prefill 调度综合考虑三个因素:
- 前缀命中长度:命中越长,计算量节省越多
- 队列等待时间:实例当前排队的预估时间
- KVCache 传输时间:若最优缓存在别的节点,需要额外传输
调度算法(Algorithm 1)对每个候选 Prefill 实例估算 TTFT:
其中:
- :从最优缓存节点传输缺失 KVCache 的时间(本地命中时为 0)
- :当前队列等待时间
- :根据离线拟合的多项式回归模型,由请求长度 和前缀命中长 估算的计算时间
最终选择预估 TTFT 最小的实例。若 TTFT 超过 SLO 上限,直接返回 HTTP 429 拒绝请求。
6.2 热点迁移与缓存负载均衡
真实负载中,不同 KVCache 块的访问频率差异极大——系统 prompt 可能被几乎所有请求访问,而某个长文档的 KVCache 可能只有一个用户使用。
Mooncake 采用启发式热点自动迁移策略(而非精确预测),核心思路是利用调度决策的副作用实现热点复制:
触发条件:当某个实例本地的最优前缀命中长度,与全局最优命中长度的比值超过阈值时,Conductor 判定该块为热点,并触发复制:
1 | if best_global_prefix_len / local_prefix_len > kvcache_balancing_threshold: |
自然涌现的复制效果:
- 请求被调度到某实例时,若远端有更好的缓存,该实例会主动拉取并本地化
- 如果不跨节点传输反而本地重算更快(即远端优势不足),则直接计算,避免不必要传输
- 两种路径都天然地将热点 KVCache 复制到更多节点
实验表明,在对话(Conversation)和工具/Agent 负载下,排名前 100 的热点 KVCache 块(通常是系统 prompt)会在系统稳定后几乎复制到每个 Prefill 实例,彻底消除传输瓶颈。
7. 过载场景下的调度
与多数 LLM 服务研究假设资源充足不同,Kimi 在高峰期持续面临严重过载。Mooncake 在解耦架构下面临传统系统没有的独特挑战。
7.1 过载的定义与度量
Mooncake 使用 SLO 满足率而非简单的请求数量来度量负载:
- Prefill 负载:当前队列中的请求预计 TTFT 是否超限
- Decode 负载:当前 batch 中预计 TBT 是否超限
7.2 早拒绝(Early Rejection)
在解耦架构中,Prefill 完成后请求才进入 Decode 队列。若此时 Decode 已满载,该请求会被拒绝——但 Prefill 的计算资源已经白白浪费。
早拒绝策略:在请求到达时,同时评估当前 Prefill 和 Decode 的负载,以二者的较大值决定是否接受请求:
1 | if prefill_load > threshold or decode_load > threshold: |
这将 Decode 的负载判断提前到 Prefill 开始之前,避免浪费。
7.3 早拒绝引发的负载振荡问题
然而,早拒绝策略引入了新问题——Prefill 和 Decode 实例之间的负载产生反相振荡:

图 6:早拒绝引入的 Prefill/Decode 负载振荡。两个实例的负载周期性地交替达到峰值和低谷,严重浪费资源。
振荡的根本原因是Prefill 到 Decode 之间存在时间差:
- 阶段 1:Prefill 和 Decode 负载都低 → Conductor 大量接受请求 → Prefill 满载
- 阶段 2:Prefill 处理的请求涌入 Decode → Decode 满载 → Conductor 开始拒绝 → Prefill 空转
- 阶段 3:Decode 消化完积压,负载下降 → Conductor 再次大量接受 → Prefill 再次满载
- 如此循环往复,形成严重的防相位振荡
7.4 基于预测的早拒绝(Prediction-based Early Rejection)

图 7:基于预测的早拒绝(右图)相比朴素早拒绝(左图),显著消除了负载振荡,使 Prefill 和 Decode 负载趋于平稳。
解决方案是预测未来的 Decode 负载,而非基于当前时刻的快照做决策。
Mooncake 目前采用系统级预测(而非请求级预测):
- 假设每个请求的 Decode 阶段耗时均为固定值
- 对于当前时刻 ,将 Prefill 实例在未来 内预计完成的请求加入"模拟 Decode 队列"
- 将"模拟 Decode 队列"中已超过 的请求移除
- 计算模拟队列的平均 TBT/SLO 比值,作为未来 Decode 负载预测
基于这个预测值决定是否接受新请求,从源头切断振荡的正反馈环。
8. 架构设计的关键权衡总结
| 设计选择 | 问题背景 | Mooncake 的方案 | 关键 Trade-off |
|---|---|---|---|
| P/D 分离 | Chunked Prefill 是否可替代 P/D 分离? | 保持分离,但短请求可 inline | 架构复杂性 vs. SLO 可预测性 |
| 全局 KVCache 池 | 本地缓存能否满足需求? | 全集群 DRAM/SSD 汇聚为全局池 | 传输带宽 vs. 计算节省 |
| CPP vs. SP | 多节点如何处理超长 Prefill? | Chunked Pipeline Parallelism | MFU vs. 动态弹性 |
| 逐层传输 | 如何降低 Prefill 的 VRAM 占用? | KVCache 按层异步流出 | 实现复杂度 vs. 调度简化 |
| 热点复制 | 如何避免热点 KVCache 传输拥塞? | 启发式调度副作用自然触发复制 | 内存占用 vs. 访问延迟 |
| 基于预测的早拒绝 | P/D 解耦导致负载振荡如何解决? | 预测未来 Decode 负载 | 预测精度 vs. 振荡抑制 |
9. 总结
Mooncake 提出了一套以 KVCache 为调度核心的 LLM 推理解耦架构,其核心贡献可以归纳为:
-
"用存储换计算"的系统性践行:通过整合集群闲置的 CPU DRAM/SSD 构建全局分布式 KVCache 池,利用 RDMA 高速网络实现跨节点前缀缓存复用,突破了单机缓存容量瓶颈
-
精细的 P/D 解耦:将 Prefill 和 Decode 阶段分配给不同资源池,消除相互干扰,使两个阶段都能独立优化,同时通过 layer-wise 异步传输最大化管线并行
-
CPP 机制:以低通信开销的流水线并行替代序列并行,在自然适配长短上下文的同时保持高 MFU
-
KVCache 感知的全局调度:Conductor 综合考虑缓存命中、传输时间、队列深度,并通过调度副作用自动触发热点复制,实现无需精确预测的缓存负载均衡
-
过载场景的预测性早拒绝:通过预测未来 Decode 负载而非响应当前快照,从根本上消除解耦架构中的负载振荡问题
这套架构使 Kimi 在 A800 集群上相比原有 vLLM 方案能处理 115% 更多请求,每日处理超过 1000 亿 tokens,在以长上下文为核心场景的 LLM 服务领域树立了工程实践的新标杆。
参考文献:
- Ruoyu Qin et al. “Mooncake: A KVCache-centric Disaggregated Architecture for LLM Serving.” arXiv:2407.00079v4, 2024.
- Ruoyu Qin et al. “MOONCAKE: Trading More Storage for Less Computation.” FAST '25, 2025.



