Mooncake: A KVCache-centric Disaggregated Architecture for LLM Serving
TL;DR 精炼摘要
Mooncake 是一种以 KVCache 为中心的解耦式架构,大幅提升了 LLM 服务的有效吞吐量,同时满足延迟相关的服务水平目标(SLO)。通过分离预填充和解码阶段,并利用 GPU 集群的闲置资源,Mooncake 在长上下文场景中实现了高达 525% 的吞吐量提升,能处理 75% 更多请求。
摘要
Mooncake is the serving platform for Kimi, a leading LLM service provided by Moonshot AI. It features a KVCache-centric disaggregated architecture that separates the prefill and decoding clusters. It also leverages the underutilized CPU, DRAM, and SSD resources of the GPU cluster to implement a disaggregated cache of KVCache. The core of Mooncake is its KVCache-centric scheduler, which balances maximizing overall effective throughput while meeting latency-related Service Level Objectives (SLOs). Unlike traditional studies that assume all requests will be processed, Mooncake faces challenges due to highly overloaded scenarios. To mitigate these, we developed a prediction-based early rejection policy. Experiments show that Mooncake excels in long-context scenarios. Compared to the baseline method, Mooncake can achieve up to a 525% increase in throughput in certain simulated scenarios while adhering to SLOs. Under real workloads, Mooncake's innovative architecture enables Kimi to handle 75% more requests.
论文精读
中文精读
1. 论文基本信息
1.1. 标题
Mooncake: A KVCache-centric Disaggregated Architecture for LLM Serving (月饼:一种以 KVCache 为中心的解耦式 LLM 服务架构)
1.2. 作者
Ruoyu Qin, Zheming Li, Weiran He, Mingxing Zhang, Yongwei Wu, Weimin Zheng, Xinran Xu (Moonshot AI 与 Tsinghua University)
1.3. 发表期刊/会议
该论文发布在 arXiv,是一个预印本平台。arXiv 在学术界具有很高的影响力,通常用于快速分享最新的研究成果,但未经同行评审。
1.4. 发表年份
2024 年 6 月 24 日 (UTC)
1.5. 摘要
Mooncake 是 Kimi 的大型语言模型(LLM)服务平台,Kimi 是月之暗面公司提供的一个领先的 LLM 服务。Mooncake 采用以 KVCache 为中心的解耦架构(KVCache-centric disaggregated architecture),将预填充阶段(prefill)和解码阶段(decoding)的集群分开。它还利用 GPU 集群中未充分利用的 CPU、DRAM 和 SSD 资源来实现 KVCache 的解耦缓存(disaggregated cache)。Mooncake 的核心是其以 KVCache 为中心的调度器(KVCache-centric scheduler),该调度器旨在平衡最大化整体有效吞吐量(effective throughput)与满足与延迟相关的服务水平目标(Service Level Objectives, SLOs)。与假设所有请求都将被处理的传统研究不同,Mooncake 面对高度过载(highly overloaded)的场景。为了缓解这些问题,我们开发了一种基于预测的早期拒绝策略(prediction-based early rejection policy)。实验表明,Mooncake 在长上下文(long-context)场景中表现出色。与基线方法相比,在某些模拟场景下,Mooncake 可以在遵守 SLO 的前提下,实现高达 525% 的吞吐量提升。在真实工作负载下,Mooncake 的创新架构使 Kimi 能够处理多达 75% 的请求。
1.6. 原文链接
原文链接: https://arxiv.org/abs/2407.00079 PDF 链接: https://arxiv.org/pdf/2407.00079v4.pdf
2. 整体概括
2.1. 研究背景与动机
随着大型语言模型(LLMs)在各个领域的广泛应用,LLM 服务的工作负载变得日益多样化,请求的输入/输出长度、到达频率和分布各不相同,并且对服务水平目标(SLOs)提出了不同的要求,例如首令牌时间(Time to First Token, TTFT)和令牌间隔时间(Time Between Tokens, TBT)。作为模型即服务(Model as a Service, MaaS)提供商,Kimi 面临的核心挑战是如何在满足这些延迟 SLO 的同时,最大化整体有效吞吐量(effective throughput)。
现有研究在 LLM 服务效率方面已取得进展,例如通过连续批处理(continuous batching)和 PagedAttention 等技术来优化 GPU 内存管理和利用率。同时,一些研究者也提出了将 LLM 推理的预填充(prefill)阶段和解码(decoding)阶段分离的解耦架构(disaggregated architecture),因为这两个阶段具有截然不同的计算特性。然而,当前研究仍存在一些空白:
-
资源利用率问题: 尽管
GPU服务器通常是高度集成的节点,但其中CPU、DRAM和SSD等资源在LLM服务中往往未被充分利用。 -
KVCache 调度复杂性:
KVCache的调度对于提高吞吐量至关重要(例如通过复用KVCache和最大化批处理量),但这些优化可能与延迟SLO相冲突。 -
过载场景的挑战: 大多数现有研究假设资源充足,并侧重于提高资源利用率。然而,在
GPU供应受限且用户请求量快速增长的现实世界MaaS场景中,过载(overload)是常态,这使得调度问题变得更加复杂,并可能导致资源浪费(例如,预填充完成后才发现无法满足解码SLO而拒绝请求)。针对这些挑战,本文提出了
Mooncake,旨在通过以KVCache为中心的解耦架构和智能调度策略来解决这些问题,尤其关注长上下文处理和过载场景下的性能优化。
2.2. 核心贡献/主要发现
Mooncake 及其论文的主要贡献和发现可以总结如下:
- KVCache-centric 解耦架构: 提出了一个创新性的、以
KVCache为中心的解耦架构。它不仅将预填充集群(prefill cluster)和解码集群(decoding cluster)物理分离,还利用GPU服务器中未充分利用的CPU、DRAM和SSD资源构建了一个解耦的KVCache缓存池,极大地扩展了KVCache的容量和传输带宽。 - KVCache-centric 调度器 (
Conductor): 设计了一个名为Conductor的核心调度器,它以KVCache为中心进行全局调度。该调度器能够平衡最大化整体有效吞吐量与满足TTFT和TBT等延迟SLO。它考虑了KVCache的复用、预填充实例的负载平衡以及KVCache热点迁移等因素。 - 长上下文处理优化:
- 引入了分块流水线并行(Chunked Pipeline Parallelism, CPP)来高效处理长上下文预填充,减少跨节点通信开销并适应动态上下文长度。
- 实现了逐层预填充(Layer-wise Prefill),通过将
KVCache的传输与计算重叠,显著降低了长上下文预填充的延迟和VRAM占用。
- 过载场景下的早期拒绝策略: 针对
MaaS平台常见的过载问题,开发了基于预测的早期拒绝策略(prediction-based early rejection policy)。该策略通过在请求的预填充阶段开始前,预测其在解码阶段是否会满足SLO,从而决定是否接受请求,有效减少了因请求被拒绝而导致的计算资源浪费,并缓解了早期拒绝可能导致的负载波动问题。 - 显著的性能提升:
- 在长上下文模拟场景下,
Mooncake相较于基线方法(vLLM)在满足SLO的前提下,吞吐量最高可提升525%。 - 在真实工作负载下,
Mooncake的架构使得Kimi能够处理多达75%的请求,同时保持TBT SLO的高度满足率(接近100%),而vLLM只有57%的请求满足TBT SLO。
- 在长上下文模拟场景下,
3. 预备知识与相关工作
3.1. 基础概念
为了理解 Mooncake 提出的架构和调度策略,需要先了解 LLM 服务中的一些基本概念和挑战:
- 大型语言模型 (Large Language Models, LLMs):指参数量巨大,通常基于
Transformer架构的深度学习模型,能够理解和生成人类语言。 - Transformer 架构 (Transformer Architecture):
LLM的核心架构,由编码器(encoder)和解码器(decoder)组成,但LLM通常只使用decoder-only结构。它依赖自注意力机制(self-attention mechanism)来处理序列数据。 - 预填充阶段 (Prefill Stage):
LLM推理的第一个阶段。当用户输入一个提示(prompt)时,模型会并行处理所有输入词元(tokens),计算并生成第一个输出词元,同时将中间计算结果(键和值)存储为KVCache。这个阶段通常计算密集型,尤其对于长输入序列。 - 解码阶段 (Decoding Stage):
LLM推理的第二个阶段。模型利用预填充阶段生成的KVCache,自回归地(autoregressively)逐个生成新的输出词元。每次生成一个新词元,都会将新的键和值添加到KVCache中。这个阶段通常是内存密集型,并且需要高效的批处理。 - KVCache (键值缓存):
Transformer模型中注意力机制计算的中间结果,即键(Key)和值(Value)向量的缓存。在自回归生成过程中,为了避免重复计算历史词元的注意力,KVCache被存储并复用。它占据GPU显存(VRAM),对LLM服务的内存管理至关重要。 - 词元 (Token):文本经过分词器(tokenizer)处理后的基本单位,可以是单词、子词或字符。
LLM以词元为单位进行输入和输出。 - 连续批处理 (Continuous Batching):一种
LLM服务优化技术,旨在提高GPU利用率。它允许在GPU空闲时,将新的请求动态地添加到正在进行的批处理中,或移除已完成的请求,从而保持GPU的持续高负载。 - PagedAttention:
vLLM提出的KVCache管理技术,灵感来源于操作系统中的虚拟内存分页,将KVCache存储在非连续的内存空间中,并支持灵活的共享和重用,有效解决了KVCache碎片化和内存浪费问题。 - 服务水平目标 (Service Level Objectives, SLOs):衡量服务质量的关键指标,通常包括延迟、吞吐量、可用性等。在
LLM服务中,主要关注TTFT和TBT。 - 首令牌时间 (Time to First Token, TTFT):用户发送请求到模型生成第一个输出词元之间的时间间隔。这对于用户体验,尤其是交互式应用,至关重要。
- 令牌间隔时间 (Time Between Tokens, TBT):模型生成连续两个输出词元之间的时间间隔。这衡量了模型生成速度的流畅性。
- 模型浮点运算利用率 (Model FLOPs Utilization, MFU):衡量
GPU计算资源利用效率的指标,即GPU实际执行的浮点运算次数与理论峰值之间的比率。提高MFU通常需要更大的批处理量。 - 解耦架构 (Disaggregated Architecture):将系统中不同功能模块(如计算、存储、网络)或不同处理阶段(如预填充、解码)分离,并部署在独立的资源池上,以实现更灵活的资源分配和优化。
- 前缀缓存 (Prefix Caching):一种
KVCache复用技术。如果多个请求共享相同的前缀输入序列(例如,相同的系统提示或文档开头),那么这部分共享前缀的KVCache可以被计算一次并缓存起来,供后续请求直接使用,从而节省预填充阶段的计算开销。 - 远程直接内存访问 (Remote Direct Memory Access, RDMA):一种允许网络适配器直接访问远程服务器内存,而无需
CPU介入的技术。它能显著降低网络通信的延迟和CPU开销,对于高速数据传输(如KVCache跨节点传输)非常重要。
3.2. 前人工作
论文在介绍 Mooncake 的设计时,提及并借鉴了大量 LLM 服务领域的现有工作。理解这些工作对于把握 Mooncake 的创新点至关重要。
- LLM 服务效率优化:
- FasterTransformer [28], TensorRT-LLM [29], DeepSpeed Inference [30]:这些是生产级
LLM推理系统,通过底层优化(如算子融合、量化、高效并行策略)显著提升吞吐量。它们通常聚焦于单个模型的性能,而不涉及复杂的分布式调度。
- FasterTransformer [28], TensorRT-LLM [29], DeepSpeed Inference [30]:这些是生产级
- KVCache 管理与批处理:
- Orca [12]:通过迭代级调度(iteration-level scheduling)实现不同请求阶段的并发处理。
- vLLM [13]:利用
PagedAttention技术进行动态KVCache管理,极大地优化了内存使用和吞吐量。Mooncake在KVCache管理方面受vLLM启发,但vLLM主要支持本地KVCache缓存,且其耦合的预填充/解码设计在长上下文场景下可能受限。 - Prompt Cache [33]:预计算并存储常用
prompt的KVCache,减少推理延迟。 - SGLang [34]:利用
RadixAttention和LRU缓存机制,高效地自动共享KVCache。 - AttentionStore [35]:与
Mooncake同期的工作,提出了一个分层KVCache系统,利用成本效益高的内存和存储介质来容纳所有请求的KVCache。Mooncake在架构上与AttentionStore有相似之处,但更侧重于长上下文推理中的大规模KVCache管理和传输,以及缓存感知的全局调度。
- 解耦架构:
- Splitwise [7]:最早提出将预填充和解码阶段分离的解耦架构,通过阶段拆分提高效率。
- DistServe [8]:进一步优化资源分配和并行策略,以最大化
GPU的有效吞吐量(goodput)。 - TetriInfer [9]:结合分块预填充(chunked prefill)和两阶段解耦,并提出预测性的两阶段调度算法来优化资源利用。
- 这些工作与
Mooncake的设计理念相似,都认为预填充和解码阶段需要分离以优化资源,但Mooncake更进一步,将KVCache置于调度的中心,并专注于过载场景。
- 长上下文处理:
- Loongserve [14]:通过弹性序列并行(elastic sequence parallelism)高效服务长上下文
LLM。虽然与Mooncake的分块流水线并行(CPP)目标相似,但Mooncake的CPP旨在减少跨节点通信和简化动态调整。
- Loongserve [14]:通过弹性序列并行(elastic sequence parallelism)高效服务长上下文
- 注意力机制核心公式 (Attention Mechanism Core Formula):
由于
LLM基于Transformer架构,其核心是注意力机制。即使论文未复述,理解它对于理解KVCache的作用至关重要。 自注意力机制(Self-Attention Mechanism)的计算公式如下: 其中:-
(Query):查询矩阵,由输入序列通过线性变换得到。
-
(Key):键矩阵,由输入序列通过线性变换得到。
-
(Value):值矩阵,由输入序列通过线性变换得到。
-
:查询和键的点积,表示查询与每个键的相似度。
-
:缩放因子,用于防止点积结果过大,其中 是键向量的维度。
-
:softmax 函数,将注意力分数归一化为概率分布。
-
:输出的注意力加权值,是值矩阵 的加权和。
在自回归解码阶段,
KVCache存储的就是历史词元的 和 矩阵,从而避免了在生成每个新词元时重复计算整个历史序列的 和 。
-
3.3. 技术演进
LLM 服务技术经历了从早期简单的 Transformer 推理框架到如今高度优化的分布式系统的演进:
- 早期优化阶段: 聚焦于单个
GPU或节点内的优化,如FasterTransformer、TensorRT-LLM通过算子融合、批处理、量化等手段提升单卡或单机性能。 - 批处理与内存管理: 随着
LLM规模增大,KVCache成为瓶颈。Orca、vLLM等引入了连续批处理和PagedAttention等技术,高效管理KVCache,显著提升了GPU利用率和吞吐量。 - 解耦架构的兴起: 认识到预填充和解码阶段的计算特性差异巨大,
Splitwise、DistServe、TetriInfer等工作提出了将这两个阶段分离的解耦架构,以实现更灵活的资源配置和调度。 - KVCache-centric 与过载处理:
Mooncake在解耦架构的基础上,进一步将KVCache推向中心地位,不仅利用闲置资源构建大规模KVCache缓存池,还设计了KVCache-centric调度器,并首次系统地解决了MaaS环境中普遍存在的过载问题,提出了基于预测的早期拒绝策略。
3.4. 差异化分析
Mooncake 与现有工作的主要区别和创新点在于:
- KVCache 的核心地位:
Mooncake是第一个明确提出并围绕KVCache构建整个解耦架构和调度策略的系统。它不仅仅是利用KVCache,而是将其视为系统优化的核心,通过解耦缓存池、热点迁移等手段最大化其价值。 - 利用未充分利用的资源:
Mooncake明确指出并利用了GPU集群中CPU、DRAM和SSD等未充分利用的资源来构建一个大规模的解耦KVCache缓存池,这是其他解耦系统较少强调的。 - 长上下文处理的独特方案: 提出了分块流水线并行(CPP)和逐层预填充(Layer-wise Prefill),这些技术旨在高效处理超长上下文,同时减少网络开销和
VRAM占用,这与传统的序列并行(SP)或张量并行(TP)在分布式环境中的使用有所不同。 - 聚焦过载场景: 大多数现有研究假设资源充足,
Mooncake则针对MaaS平台常见的过载场景,开发了预测式早期拒绝策略,以最小化资源浪费和优化系统稳定性。这是对LLM服务调度领域的一个重要补充。 - 端到端性能与
SLO导向:Mooncake不仅仅追求吞吐量最大化,而是严格在满足TTFT和TBT SLO的前提下进行优化,并通过“有效吞吐量”(goodput)的概念来衡量性能。
4. 方法论
Mooncake 的方法论围绕其 KVCache-centric 的解耦架构展开,旨在通过智能调度和资源管理,在长上下文和过载场景下最大化有效吞吐量并满足 SLO。
4.1. 方法原理
Mooncake 的核心思想是,KVCache 是 LLM 推理中连接预填充和解码阶段的关键桥梁,也是资源消耗的重要组成部分。通过将 KVCache 作为中心,系统可以实现以下目标:
- 解耦预填充与解码: 充分利用两个阶段不同的计算特性,分别优化资源。预填充阶段计算密集,适合并行处理和大规模
KVCache复用;解码阶段内存带宽密集,适合连续批处理以提高MFU。 - KVCache 的高效管理与复用: 将
KVCache存储在CPU内存和SSD中,利用GPU集群中未充分利用的资源,提供更大的缓存容量。同时,通过前缀缓存和热点迁移,最大化KVCache的复用率,减少重复计算。 - 智能调度策略: 设计一个全局调度器(
Conductor),它基于KVCache的分布和实例负载,做出最优的调度决策,平衡吞吐量和SLO。 - 过载场景韧性: 针对真实世界的过载情况,引入预测式早期拒绝机制,避免资源浪费,并稳定系统负载。
4.2. 核心方法详解
4.2.1. Mooncake 架构概览
Mooncake 的整体架构如 Figure 1 所示,它是一个 KVCache-centric 的解耦架构,主要由以下核心组件构成:
该图像是Mooncake架构的示意图,展示了KVCache中心调度器的组成部分,包括预填充实例、KVCache池和解码池。图中显示了不同调度器的功能,如缓存感知的预填充调度器、KVCache平衡调度器和负载均衡解码调度器,强调了各个组件之间的资源分配和相互作用。
Figure 1: Mooncake Architecture.
- Prefill Cluster (预填充集群): 负责处理请求的预填充阶段。通常由多个
GPU节点组成,这些节点针对并行计算和长上下文处理进行优化。 - Decoding Cluster (解码集群): 负责处理请求的解码阶段。也由多个
GPU节点组成,这些节点针对连续批处理和高效内存访问进行优化。 - KVCache Pool (KVCache 池): 这是一个解耦的缓存存储,它利用
GPU集群中未充分利用的CPUDRAM和SSD资源来存储KVCache。这提供了巨大的容量和传输带宽,使得KVCache可以在不同节点之间高效共享和迁移。 - Conductor (调度器):
Mooncake的全局调度核心。它负责:- 接收新请求。
- 根据当前
KVCache分布、预填充和解码实例的负载以及SLO要求,选择合适的预填充和解码实例。 - 预测
KVCache块的未来使用情况,并执行KVCache的交换(swapping)和复制(replication)操作,以避免获取瓶颈并降低存储成本。 - 在过载场景下,执行早期拒绝策略。
4.2.2. KVCache 池设计
KVCache 池是 Mooncake 能够实现高效 KVCache 复用和解耦架构的关键。
该图像是示意图,展示了 KVCache 池在 CPU 内存中的组织结构。图中展示了多个 Token 块和对应的哈希值,区分了前缀缓存块、增量缓存块和未分配缓存块。每个缓存块都附有通过自身哈希及其前缀确定的哈希值,以便进行去重处理。该设计支持 KVCache 的存储、读取和加载,旨在优化 Mooncake 的缓存管理效率。
Figure 3: The KVCache pool in CPU memory. Each block is attached with a hash value determined by both its own hash and its prefix for deduplication.
如 Figure 3 所示,KVCache 在 CPU 内存中以分页的块(paged blocks)形式存储。每个块都附带一个哈希值,这个哈希值由其自身内容的哈希和其前缀的哈希共同决定,用于实现去重(deduplication)。这种设计使得相同的前缀 KVCache 可以被识别并共享。
- 存储介质: 利用
CPUDRAM和SSD作为KVCache的存储介质,相较于昂贵的GPU VRAM,提供了更大的容量和更低的成本。 - 缓存算法: 根据请求模式,可以采用
LRU(Least Recently Used,最近最少使用)、LFU(Least Frequently Used,最不经常使用) 或基于请求特征的算法进行缓存淘汰。 Messenger(消息传递器):Messenger是一个独立的组件,部署在每个节点上,负责管理和传输这些KVCache块。它利用GPUDirect RDMA等技术,实现KVCache块在CPU之间和CPU与GPU之间的高速、跨机器传输。- 开放
Context Caching API: 这种架构还允许Mooncake向外部用户提供Context Caching API,以实现更高级别的KVCache复用。
4.2.3. 请求工作流
一个请求在 Mooncake 架构中的典型工作流如 Figure 4 所示,由 Conductor 协调完成:
该图像是图表,展示了推理实例的工作流程。对于预填充实例,KVCache 层的加载和存储操作是逐层进行并与预填充计算并行,以减轻传输开销。对于解码实例,异步加载与 GPU 解码并发进行,以防止 GPU 空闲时间。
Figure 4: Workflow of inference instances. (*) For prefill instances, the load and store operations of the KVCache layer are performed layer-by-layer and in parallel with the prefill computation to mitigate transmission overhead (see . (†) For decoding instances, asynchronous loading is performed concurrently with GPU decoding to prevent GPU idle time.
-
KVCache 复用 (KVCache Reuse):
- 当一个新请求到达并完成词元化(tokenizing)后,
Conductor会为该请求选择一对预填充实例和解码实例。 - 选定的预填充实例接收请求,其中包含原始输入、可复用的前缀缓存(prefix cache)的块
ID,以及为该请求分配的完整缓存的块ID。 - 预填充实例根据前缀缓存块
ID,从远程CPU内存加载前缀缓存到GPU内存,以引导请求处理。如果不存在可复用的前缀缓存,则跳过此步骤。 - 此步骤的选择需要平衡三个目标:尽可能多地复用
KVCache、平衡不同预填充节点的负载以及保证TTFT SLO。
- 当一个新请求到达并完成词元化(tokenizing)后,
-
增量预填充 (Incremental Prefill):
- 预填充实例(或实例组)使用已加载的前缀缓存完成预填充阶段,并将新生成的增量
KVCache存回CPU内存。 - 如果未缓存的输入词元数量超过预设阈值
prefill_chunk,预填充阶段将被拆分为多个块,并以流水线(pipeline)方式执行。
- 预填充实例(或实例组)使用已加载的前缀缓存完成预填充阶段,并将新生成的增量
-
KVCache 传输 (KVCache Transfer):
- 上文提到的
Messenger服务部署在每个节点上,管理和传输KVCache。每个Messenger作为独立进程运行,接收信号以实现高速、跨机器的KVCache传输。 - 此步骤是异步执行的,并与增量预填充步骤重叠。预填充实例会逐层(layer-by-layer)将其生成的
KVCache流式传输到目标解码节点(destination decoding node)的CPU内存,以减少等待时间。
- 上文提到的
-
解码 (Decoding):
- 一旦所有
KVCache都被接收到解码节点的CPU DRAM中,该请求就会以连续批处理(continuous batching)的方式加入下一个批次。 Conductor会根据解码节点的当前负载预先选择它,以确保不会违反TBT SLO。- 然而,本地调度器会再次检查
SLO,因为预期的负载在预填充阶段后可能发生变化。如果此时发现无法满足SLO,请求可能会被拒绝,从而导致预填充阶段的计算成本被浪费。
- 一旦所有
4.2.4. 预填充池实现
4.2.4.1. 多节点预填充 (Multi-node Prefill)
为了处理日益增长的长上下文 LLM 请求(如 128K 甚至 1M 词元),Mooncake 引入了 分块流水线并行 (Chunked Pipeline Parallelism, CPP) 来加速预填充。
- 长上下文的挑战: 对于长上下文,输入词元数量可能是输出词元的 10 到 100 倍,使得
TTFT优化至关重要。虽然可以使用多个GPU节点并行处理,但传统的张量并行(Tensor Parallelism, TP)在跨节点时需要昂贵的RDMAall-reduce操作,降低MFU。序列并行(Sequence Parallelism, SP)虽然减少了网络消耗,但在MFU上仍不如单节点TP,且需要复杂的动态伸缩。 - CPP 的方案:
Mooncake利用decoder-onlyTransformer的自回归特性,将预填充集群中的每 个节点分组为一个流水线预填充节点组。对于每个请求,其输入词元被分成多个块,每个块的长度不超过prefill_chunk。同一请求的不同块可以由不同的节点同时处理,从而实现并行化并减少TTFT。 - CPP 的优势:
- 高效通信: 类似于训练中的流水线并行,
CPP只在每个流水线阶段的边界处进行跨节点通信,这部分通信可以与计算重叠,从而带来更好的MFU,并减少与KVCache传输的网络资源竞争。 - 适应性强:
CPP自然适用于短上下文和长上下文,对短上下文预填充没有显著开销,避免了频繁动态调整节点分区。
- 高效通信: 类似于训练中的流水线并行,
4.2.4.2. 逐层预填充 (Layer-wise Prefill)
除了计算能力,VRAM 的有限性也是一个宝贵资源。Mooncake 旨在最小化 KVCache 在 VRAM 中的占用。
- 原理: 由于预填充是逐层处理且受计算限制的,因此可以将
KVCache的传输和存储与计算重叠。 - 实现: 在
Mooncake中,KVCache的加载和存储是异步执行的。- 在每个层(layer)的注意力计算开始前,模型等待该层
KVCache的异步加载完成,并触发下一层KVCache的异步加载。 - 注意力计算完成后,启动该层
KVCache的异步存储。 - 所有层计算完成后,等待所有异步存储操作完成。
- 在每个层(layer)的注意力计算开始前,模型等待该层
- 优势:
-
降低
VRAM占用: 这种传输重叠使得预填充实例的执行时间大致等于KVCache加载时间或标准预填充时间,具体取决于前缀缓存占输入长度的比例。这允许我们在预填充调度中,只要VRAM能容纳单个请求,就可以忽略其大小。 -
减少延迟: 如 Figure 7 所示,逐层预填充能有效减少长上下文请求的延迟。
该图像是一个图表,展示了不同请求长度下存储KVCache的延迟。蓝色条表示序列化延迟,黄色条表示分层延迟。随着序列长度增加,延迟显著上升,尤其在128000时达到最高值。
-
Figure 7: Latency of storing KVCache of different request lengths (Layer-wise latency refers to the difference in latency between Layer-wise Prefill and Prefill without storing KVCache).
Figure 7 展示了存储不同请求长度的 KVCache 的延迟。其中,“Layer-wise latency”指的是逐层预填充(Layer-wise Prefill)与不存储 KVCache 的预填充之间的延迟差异。从图中可以看出,随着请求长度的增加,逐层预填充带来的延迟增量是可控的,证明了其在长上下文场景下的有效性。
4.2.5. KVCache-centric 调度
本节主要讨论 Conductor 如何在正常情况下调度请求和 KVCache 块。
4.2.5.1. 预填充全局调度 (Prefill Global Scheduling)
传统的 LLM 服务调度通常采用负载均衡策略。而 Mooncake 的调度算法(如 Algorithm 1 所示)在此基础上,额外考虑了前缀缓存命中长度和可复用 KVCache 块的分布。
Input: prefill instance pool P, decoding instance pool D, request R, cache block size B.
Output: the prefill and decoding instances (p, d) to process R.
1: block_keys ← PrefixHash(R.prompt_tokens, B)
2: TTFT ← inf
3: p ← ∅
4: best_prefix_len, best_matched_instance ← FindBestPrefixMatch(P, block_keys)
5: for instance ∈ P do
6: prefix_len ← instance.prefix_len
7: T_queue ← EstimatePrefillQueueTime(instance)
8: if best_prefix_len / prefix_len < kvcache_balancing_threshold then # Cache-aware prefill scheduling
9: T_prefill ← EstimatePrefillExecutionTime(len(R.prompt_tokens), prefix_len)
10: if TTFT > T_queue + T_prefill then
11: TTFT ← T_queue + T_prefill
12: p ← instance
13: end if
14: else: # Cache-aware and -balancing prefill scheduling
15: transfer_len ← best_prefix_len - prefix_len
16: T_transfer ← EstimateKVCacheTransferTime(instance, best_matched_instance, transfer_len)
17: T_prefill ← EstimatePrefillExecutionTime(len(R.prompt_tokens), best_prefix_len)
18: if TTFT > T_transfer + T_queue + T_prefill then
19: TTFT ← T_transfer + T_queue + T_prefill
20: p ← instance
21: end if
22: end if
23: end for
24: d ← SelectDecodingInstance(D) # Load-balancing decoding scheduling
25: if TTFT > TTFT_SLO or TBT > TBT_SLO then
26: reject R; return
27: end if
28: if best_prefix_len > kvcache_balancing_threshold then
29: TransferKVCache(best_matched_instance, p) # KVCache hot-spot migration
30: end if
31: return (p, d)
算法 1: 缓存感知预填充调度
- 输入: 预填充实例池 ,解码实例池 ,请求 ,缓存块大小 。
- 输出: 处理请求 的预填充实例 和解码实例 。
算法步骤详解:
- 生成块键:
block_keysPrefixHash(.prompt_tokens, )。对于每个新请求,其输入词元被划分为多个块,并为每个块计算一个哈希键。这个哈希键包含了当前块及其所有前置块的哈希信息,用于识别前缀缓存关系。 - 初始化:
TTFT(无穷大), (空)。 - 查找最佳前缀匹配: (
best_prefix_len,best_matched_instance)FindBestPrefixMatch(,block_keys)。Conductor检查所有预填充实例,找到能提供最长前缀匹配 (best_prefix_len) 的实例 (best_matched_instance)。 - 遍历预填充实例选择:
- 对于池 中的每个
instance:prefix_leninstance.prefix_len。获取当前实例能提供的前缀匹配长度。T_queueEstimatePrefillQueueTime(instance)。估算请求在该实例的预填充队列中等待的时间。- 缓存感知预填充调度 (Cache-aware prefill scheduling):
- 如果
best_prefix_len / prefix_len < kvcache_balancing_threshold:这意味着当前实例的本地前缀匹配长度相对较短(或最佳匹配相对较长),或者说,本地计算的收益可能更高,或者无需进行昂贵的KVCache传输。 T_prefillEstimatePrefillExecutionTime(len(.prompt_tokens),prefix_len)。估算在该实例上执行预填充所需的时间,基于请求总长度和当前实例可用的前缀缓存长度。- 如果
T_queue + T_prefill小于当前的TTFT最优值,则更新TTFT和最佳预填充实例 。
- 如果
- 缓存感知与平衡预填充调度 (Cache-aware and -balancing prefill scheduling):
- 否则:这意味着最佳前缀匹配很长,且当前实例没有最佳匹配,可能需要传输
KVCache。 transfer_lenbest_prefix_len - prefix_len。计算需要传输的KVCache长度。T_transferEstimateKVCacheTransferTime(instance,best_matched_instance,transfer_len)。估算从best_matched_instance传输KVCache到当前instance所需的时间。T_prefillEstimatePrefillExecutionTime(len(.prompt_tokens),best_prefix_len)。估算在该实例上执行预填充所需的时间,假设它获得了最长的前缀缓存。- 如果
T_transfer + T_queue + T_prefill小于当前的TTFT最优值,则更新TTFT和最佳预填充实例 。
- 否则:这意味着最佳前缀匹配很长,且当前实例没有最佳匹配,可能需要传输
- 对于池 中的每个
- 解码实例选择:
SelectDecodingInstance()。Conductor基于负载均衡策略选择一个解码实例。 - SLO 检查与拒绝: 如果
TTFT > TTFT_SLO或TBT > TBT_SLO,则拒绝请求 并返回HTTP 429 Too Many Requests状态码。 - KVCache 热点迁移 (KVCache hot-spot migration): 如果
best_prefix_len > kvcache_balancing_threshold,则TransferKVCache(best_matched_instance, )。这表示如果存在很长的最佳前缀匹配,并且该匹配的长度超过了某个平衡阈值,则将该KVCache从其当前持有者best_matched_instance传输到选定的预填充实例 。这有助于实现KVCache的热点迁移和复制。 - 返回: 返回选定的预填充实例 和解码实例 。
工程实现细节:
- 预填充时间预测: 采用基于离线测试数据的预测模型,根据请求长度和前缀缓存命中长度估算预填充持续时间。
- 排队时间计算: 通过聚合所有排队请求的预填充时间来计算当前请求的排队时间。
- 传输时间预测: 难度较大,因为它不仅取决于数据大小,还受网络状态和拥塞影响。这也促使了
KVCache热点块的复制。
4.2.5.2. 缓存负载均衡 (Cache Load Balancing)
为了解决 KVCache 块流行度不均导致的热点问题和传输拥塞,Mooncake 提出了启发式自动热点迁移方案。
- 问题: 每个预填充机器维护自己的本地前缀缓存,但不同缓存的使用频率差异巨大(例如系统提示高频使用,特定文档缓存低频使用)。
- 策略:
- 当
Conductor因实例负载高而未能将请求路由到具有最长前缀缓存的预填充实例时,如果估算的额外预填充时间短于传输时间,Conductor会将缓存位置和请求转发给备用实例。这个备用实例会主动从持有者那里获取KVCache并存储到本地。 - 如果最佳远程前缀匹配长度不大于当前本地可复用前缀乘以一个阈值(
kvcache_balancing_threshold),则倾向于直接计算输入词元而非传输KVCache。
- 当
- 效果: 这两种策略不仅减少了请求的预填充时间,还促进了热点缓存的自动复制,使其更广泛地分布在多台机器上,从而实现缓存负载均衡。
4.2.6. 过载导向调度 (Overload-oriented Scheduling)
大多数现有 LLM 服务工作假设所有请求都将被处理,并侧重于吞吐量或 TTFT/TBT 优化。然而,在 MaaS 平台中,过载是常态。Mooncake 引入了过载导向调度来处理这类场景。
4.2.6.1. 过载场景下的调度 (Scheduling in Overload Scenarios)
- 系统负载定义: 在
Mooncake的解耦架构中,预填充和解码阶段独立处理,因此负载通过SLO满足度来衡量。- :请求的
TTFT SLO约束。 - :请求的
TBT SLO约束。 - 预填充和解码实例的负载通过将其预测的最大
TTFT和TBT与 和 进行比较来确定。
- :请求的
- 关键决策: 调度需要做出两个关键决策:
- 是否接受预填充阶段的请求,基于预填充实例的负载。
- 是否继续进行解码阶段,基于解码实例的负载。
4.2.6.2. 早期拒绝 (Early Rejection)
-
问题: 如果一个请求在完成预填充阶段后,因为解码实例负载过高而被拒绝,那么预填充阶段消耗的计算资源就浪费了。
-
策略: 早期拒绝策略将解码实例的负载评估提前到预填充阶段开始之前。当请求到达时,
Conductor基于预填充和解码池中较高的负载来评估是否接受请求。 -
优势: 显著减少了因请求被拒绝而导致的无效计算,提高了负载均衡。
-
挑战: 早期拒绝可能导致负载波动,如 Figure 9 所示。这是由于预测解码负载和实际执行之间存在时间差。
该图像是图表,展示了在20分钟内预填充实例(Prefill)和解码实例(Decoding)的负载变化情况,表示在未使用基于预测的提前拒绝策略之前的性能波动。
Figure 9: The load of prefill and decoding instances over 20 minutes, before using the predictionbased early rejection. Figure 9 展示了在使用基于预测的早期拒绝策略之前,预填充实例和解码实例在 20 分钟内的负载情况。图中的曲线显示,预填充(Prefill)和解码(Decoding)实例的负载呈现出明显的反向波动,表明两者之间存在时间滞后和资源利用不平衡。
4.2.6.3. 基于预测的早期拒绝 (Early Rejection Based on Prediction)
为了解决早期拒绝导致的负载波动问题,Mooncake 提出了基于预测的早期拒绝框架。
该图像是图表,展示了应用早期拒绝和基于预测的早期拒绝时的实例负载情况。图中的四个阶段分别展示了预填请求和解码请求在不同时间段的负载变化,并通过星号和箭头指示了接收和拒绝的决策。
Figure 10: Instance load when applying Early Rejection and Early Rejection Based on Prediction. Figure 10 对比了应用早期拒绝(Early Rejection)和基于预测的早期拒绝(Early Rejection Based on Prediction)时实例的负载情况。图 (a) 展示了早期拒绝策略下预填充和解码实例负载的剧烈波动,而图 (b) 展示了基于预测的早期拒绝策略如何缓解这种波动,使负载更加平稳。
- 原理: 该框架预测请求预填充阶段完成后的解码负载,并据此决定是否接受请求。
- 预测方法:
- 请求级别预测 (Request level): 预测每个请求的输出长度是挑战,因为高成本或低准确性。如果能准确预测输出长度,就能更精确地估计
TTFT和TBT。 - 系统级别预测 (System level):
Mooncake目前采用的是系统级别预测。它不试图预测单个请求的完成时间,而是估算指定时间后实例的总体批处理量或TBT状态。- 具体实现: 假设每个请求的解码阶段都花费统一的时间 。在给定时间 :
- 由预填充实例在 时刻完成的请求被添加到统一的解码实例中。
- 在 之前执行时间超过 的请求将从解码实例中移除。
- 计算所有解码实例的平均
TBT比率与 ,以预测负载。
- 具体实现: 假设每个请求的解码阶段都花费统一的时间 。在给定时间 :
- 请求级别预测 (Request level): 预测每个请求的输出长度是挑战,因为高成本或低准确性。如果能准确预测输出长度,就能更精确地估计
- 优势: 缓解了早期拒绝带来的负载波动,提高了资源利用率。
5. 实验设置
5.1. 数据集
为了全面评估 Mooncake 的性能,实验采用了多种数据集,包括公共数据集、模拟数据和真实工作负载的重放数据。所有实验均使用一个遵循 LLaMA2-70B 架构的虚拟模型进行,以保护专有信息并方便复现。
以下是原文 Table 2 的数据集详情:
| Dataset | Avg Input Length | Avg Output Length | Cache Ratio | Arrival Pattern |
|---|---|---|---|---|
| ArXiv Summarization [26] | 8088 | 229 | ~0% | Poisson Process |
| L-Eval [27] | 19019 | 72 | >80% | Poisson Process |
| Simulated Data | 16k, 32k, 64k, 128k | 512 | 50% | Poisson Process |
| Real Data | 7955 | 194 | ~50% | Timestamp-based |
以下是原文 Table 2 的结果:
- ArXiv Summarization [26]:
- 平均输入长度:8088 词元 (tokens)
- 平均输出长度:229 词元
- 缓存命中率:~0% (该数据集的请求通常不共享前缀,因此
KVCache复用率低) - 请求到达模式:泊松过程 (Poisson Process)
- L-Eval [27]:
- 平均输入长度:19019 词元
- 平均输出长度:72 词元
- 缓存命中率:>80% (该数据集可能包含大量共享前缀的请求,
KVCache复用率高) - 请求到达模式:泊松过程
- Simulated Data (模拟数据):
- 输入长度:16k, 32k, 64k, 128k 词元 (专门用于测试长上下文性能)
- 平均输出长度:512 词元
- 缓存命中率:50%
- 请求到达模式:泊松过程
- Real Data (真实数据):
- 平均输入长度:7955 词元
- 平均输出长度:194 词元
- 缓存命中率:~50%
- 请求到达模式:基于时间戳 (Timestamp-based),即重放实际的请求轨迹。该数据集包含 23,000 条请求,是在 1 小时内的在线请求数据样本。
数据细节 (Listing 1):
为了保护用户隐私,真实数据轨迹经过匿名化处理,仅包含时间戳、输入长度、输出长度和哈希 ID。
{ "timestamp": 27482, "input_length": 6955, "output_length": 52, "hash_ids": [46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 2353, 2354]
{ "timestamp": 30535, "input_length": 6472, "output_length": 26, "hash_ids": [46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 2366]
}
Timestamp:请求的相对到达时间,单位毫秒。Input & Output Length:输入和输出词元数量,不含实际文本。Hash ID:描述前缀缓存关系。通过对词元块(块大小 512)及其前置块进行哈希生成,然后映射到全局唯一ID。相同的Hash ID表示可以共享KVCache。例如,上述样本中前 12 个Hash ID相同,表示可以共享 个词元的前缀缓存。
统计特征 (Figure 5 & Figure 6):
该图像是图表,展示了请求跟踪中的输入和输出长度分布。左侧的蓝色直方图表示输入长度的频率分布,右侧的绿色直方图表示输出长度的频率分布。
Figure 5: Input and output length distributions in the request trace.
Figure 5 展示了真实请求轨迹中输入和输出长度的分布。平均输入长度为 7,590 词元,平均输出长度为 182 词元,平均输入-输出比约为 720。这反映了 Kimi 在长上下文处理方面的能力。
该图像是图表,展示了请求追踪中的块命中计数的累积分布函数(CDF)。横轴为块命中计数,纵轴为CDF值,图中显示了随着块命中计数增大,CDF逐渐接近1的趋势,反映了请求处理的效率。
Figure 6: CDF (Cumulative Distribution Function) of the block hit count in the request trace.
Figure 6 展示了块命中计数的累积分布函数(CDF)。它揭示了 KVCache 块流行度的显著不平衡,超过 50% 的缓存块未被使用,而某些块被访问数万次。这强调了复制热点块以避免传输拥塞的重要性。
缓存策略分析 (Table 1):
以下是原文 Table 1 的结果:
| Block capacity | Inf | 100000 | 50000 | 30000 | 10000 | 1000 |
| LRUCache | 0.51 | 0.51 | 0.50 | 0.48 | 0.40 | 0.30 |
| LFUCache | 0.51 | 0.51 | 0.49 | 0.43 | 0.35 | 0.30 |
| LengthAwareCache | 0.51 | 0.50 | 0.48 | 0.42 | 0.35 | 0.30 |
Table 1 比较了在不同缓存容量下 LRU、LFU 和 LengthAwareCache 三种缓存策略的命中率。随着缓存容量从 1,000 块增加到 50,000 块,缓存命中率从 30% 提高到 50%,但进一步增加容量改进不明显。LRUCache 在该数据集上表现最佳,这可能与请求利用率的时间局部性有关。
5.2. 评估指标
实验中关注在定义 SLO 下,不同系统的吞吐量表现。
- 吞吐量 (Throughput): 通过每秒请求数 (Requests per Second, RPS) 来衡量。
RPS越高,表示吞吐量越高。 - 首令牌时间 (Time to First Token, TTFT): 用户请求到达与模型生成第一个输出词元之间的时间。
- 令牌间隔时间 (Time Between Tokens, TBT): 模型生成连续两个输出词元之间的时间。
- 90th Percentile (P90): 评估
TTFT和TBT的主要指标,表示 90% 的请求满足该延迟要求。用于判断是否满足SLO。 - 服务水平目标 (Service Level Objectives, SLOs):
- :90% 的推理请求的
TTFT不超过单请求无干扰运行下的 10 倍。 - :90% 的推理请求的
TBT不超过单请求无干扰运行下的 5 倍。 - 标准化: 为便于比较,所有
TTFT和TBT值都根据其上限进行了归一化,上限设为 1.0。
- :90% 的推理请求的
- 有效吞吐量 (Goodput): 衡量的是在满足
SLO的前提下,系统成功处理的请求数量。只有完全完成执行并满足SLO的请求才被计入有效吞吐量。否则,所有已消耗的资源都被视为浪费。
5.3. 对比基线
- vLLM [13]: 作为业界最先进的开源
LLM服务系统之一,被选作主要基线。- 优点:
vLLM整合了连续批处理(continuous batching)和PagedAttention等技术,显著提高了推理吞吐量。 - 局限性:
vLLM的设计将推理请求的预填充和解码阶段耦合在一起。在长上下文场景中,这种耦合可能导致解码阶段的中断,影响TBT SLO。为应对长上下文,vLLM有时需要将请求单独处理,而非批处理。
- 优点:
5.4. 测试环境 (Testbed)
- 硬件: 实验系统部署在一个高性能计算节点集群上。
- 每个节点配置:8 块
NVIDIA-A800-SXM4-80GB GPU,每块GPU配备80GB HBM显存。 GPU互联:通过NVLINK连接。- 网络:配备支持高达
800 Gbps互联带宽的RDMA网卡。
- 每个节点配置:8 块
- 部署: 每个节点根据启动参数配置为预填充实例或解码实例。
- 模型: 使用一个遵循
LLaMA2-70B架构的虚拟模型(dummy model),以确保实验结果的可复现性并保护专有信息。
6. 实验结果与分析
6.1. 核心结果分析
6.1.1. 端到端性能
6.1.1.1. 公共数据集
本节评估了 Mooncake 和 vLLM 在 ArXiv Summarization 和 L-Eval 公共数据集上的端到端性能。
- 基线配置:
vLLM使用 4 个实例,表示为vLLM-[4M]。 Mooncake配置: 两种配置。-
Mooncake-[3P+1D]:3 个预填充实例(Prefill)和 1 个解码实例(Decoding)。 -
Mooncake-[2P+2D]:2 个预填充实例和 2 个解码实例。以下是原文 Figure 11 的结果:
该图像是图表,展示了Mooncake与vLLM在ArXiv Summarization和L-Eval数据集上的端到端实验结果。图中比较了不同请求速率下的P90 TTFT和P90 TBT,展示了Mooncake在高请求速率下的优越性能。
-
Figure 11: End-to-end experiments of Mooncake and vLLM on the ArXiv Summarization and L-Eval datasets
结果分析:
- 吞吐量提升:
- 在
ArXiv Summarization数据集上,Mooncake-[3P+1D]比vLLM-[4M]实现了20%的吞吐量提升,同时满足SLO。 - 在
L-Eval数据集上,Mooncake-[3P+1D]比vLLM-[4M]实现了40%的吞吐量提升。L-Eval数据集由于高缓存命中率(>80%),Mooncake的前缀缓存机制进一步降低了预填充时间,从而显著提升了吞吐量。
- 在
- 配置失衡影响:
Mooncake-[2P+2D]虽然TBT延迟较低,但在TTFT指标上表现不如Mooncake-[3P+1D]和vLLM-[4M]。这表明预填充和解码实例之间的负载不平衡会影响整体性能。作者指出,在实际部署中,预填充和解码实例的比例需求在一定时期内相对稳定,可以预先设置。
6.1.1.2. 模拟数据
本节使用模拟数据进行端到端实验,重点关注长上下文场景。集群配置与公共数据集实验相同。
以下是原文 Figure 12 的结果:
该图像是图表,展示了Mooncake与vLLM在不同请求速率下的TTF和TBT性能。图中分别显示了16k、32k、64k和128k提示的实验结果,比较了不同架构在负载下的表现。Mooncake在高请求速率下的表现优于vLLM,验证了其优越的架构设计。
Figure 12: End-to-end experiments of Mooncake and vLLM on simulated data.
结果分析:
- 长上下文优势: 模拟数据中的长上下文请求(如 16k, 32k, 64k, 128k 词元)显著干扰了
vLLM的解码阶段。为了应对此问题,vLLM不得不单独处理这些请求,而非进行批处理。 Mooncake性能:Mooncake即使采用批处理,其两阶段解耦设计也有效地最小化了预填充阶段对解码阶段的影响,确保TBT SLO不被打破。- 吞吐量显著提升:
Mooncake展示了显著更高的吞吐量,相比vLLM,吞吐量提升范围从50%到525%,同时满足相同的TTFT和TBT SLO约束。这验证了Mooncake在长上下文场景下的优越性。
6.1.1.3. 真实工作负载
本节使用真实请求轨迹(23,000 个请求)重放实验,以评估 Mooncake 在实际生产环境下的性能。
-
Mooncake配置: 10 个预填充实例和 10 个解码实例,表示为Mooncake-[10P+10D]。 -
vLLM配置: 20 个vLLM实例,表示为vLLM-[20M]。 -
SLO 阈值:
TTFT上限为 30 秒,TBT上限为每词元 0.1 秒。以下是原文 Figure 13 的结果:
该图像是图表,展示了Mooncake和vLLM在真实工作负载下请求的首次令牌到达时间(TTFT)和令牌之间时间(TBT)的累积分布函数(CDF)。图中的曲线显示了两个服务在性能方面的比较,Mooncake在TTFT上表现良好,符合其设计目标。
Figure 13: Request TTFT and TBT distributions of Mooncake and vLLM under real workloads
结果分析:
TTFT分布:Mooncake-[10P+10D]和vLLM-[20M]的TTFT分布几乎相同,接近100%的请求满足TTFT SLO。这表明两个系统在首令牌生成速度上表现相当。TBT分布:Mooncake-[10P+10D]的请求几乎100%满足TBT SLO。vLLM-[20M]只有大约57%的请求满足TBT SLO,并且一些请求表现出极高的TBT。这再次印证了vLLM在长上下文或高负载下,预填充对解码阶段的干扰问题。
- 处理请求量: 在此实验中,
Mooncake在满足SLO的前提下,能够处理约75%更多的请求。这突出显示了Mooncake架构在真实世界、高并发场景下的优势。
6.1.2. 过载场景下的性能
本节评估了 Mooncake 在过载场景下的性能,重点是系统能够处理的最大请求数量。为了模拟过载,将真实轨迹的重放速度提高到 2x。
以下是原文 Table 3 的结果:
| Baseline | Early Rejection | Early Rejection based on Prediction | |
| Number of rejected requests | 4183 | 3771 | 3589 |
以下是原文 Table 3 的结果:
结果分析:
- 基线策略 (
Baseline): 拒绝了 4,183 个请求。该策略在预填充和解码两个阶段开始前基于负载拒绝请求,但如果预填充阶段已完成而解码阶段被拒绝,仍会造成资源浪费。 - 早期拒绝 (
Early Rejection): 拒绝了 3,771 个请求。与基线相比,拒绝的请求数量减少,表明通过提前评估解码负载,避免了不必要的预填充计算,从而提高了系统资源的有效利用。 - 基于预测的早期拒绝 (
Early Rejection based on Prediction): 拒绝了 3,589 个请求。这是性能最佳的策略,进一步减少了被拒绝的请求数量。这证明了通过预测解码实例的未来负载,Mooncake能够缓解早期拒绝引起的负载波动,并进一步提高请求处理能力。
6.1.3. KVCache-centric 调度实验
为了验证 KVCache-centric 调度策略的有效性,作者在 Mooncake 集群上进行了调度实验,对比了不同策略。
-
集群配置: 8 个预填充实例和 8 个解码实例。
-
工作负载: 重放 23,000 个真实请求。
-
评估指标: 平均
TTFT和TTFT SLO达成率。以下是原文 Figure 8 的结果:
该图像是一个箱线图,展示了在不同调度策略下的总延迟(TTFT)。横坐标表示不同的调度策略,包括KVCache-centric、cache-aware、load-balancing和random,纵坐标为延迟时间(秒)。图中标明了服务水平目标(SLO),并且KVCache-centric策略显示了最低的延迟,达到14.36秒。
Figure 8: The prefill scheduling experiment in the Mooncake cluster.
Figure 8 展示了在 Mooncake 集群上进行的预填充调度实验结果。图中的柱状图比较了四种调度策略的平均 TTFT:随机调度(Random)、负载均衡调度(Load-balancing)、缓存感知调度(Cache-aware)和 KVCache-centric 调度。
结果分析:
TTFT表现:- 随机调度 (Random): 平均
TTFT最高(~17.15 秒),且SLO达成率最低(~70%)。 - 负载均衡调度 (Load-balancing): 平均
TTFT略低于随机调度(~16.71 秒),SLO达成率有所提高(~75%)。 - 缓存感知调度 (Cache-aware): 显著降低了平均
TTFT(~15.42 秒),SLO达成率进一步提升(~80%),表明考虑缓存复用对性能有积极影响。 KVCache-centric调度: 表现最佳,平均TTFT最低(~14.36 秒),SLO达成率最高(~85%)。这证明了KVCache-centric调度(同时考虑缓存感知和缓存负载均衡)能够显著降低请求的TTFT,并提高SLO满足率。
- 随机调度 (Random): 平均
6.2. 消融实验/参数分析
论文中没有明确标注为“消融实验”的章节,但其对不同调度策略和早期拒绝策略的对比可以视为对 Mooncake 各个组件有效性的验证:
- KVCache-centric 调度 (Figure 8): 通过对比随机、负载均衡、缓存感知以及
KVCache-centric调度,验证了KVCache感知和负载均衡机制(即KVCache-centric调度)对于降低TTFT和提升SLO满足率的有效性。这实际上是一种组件效果的消融分析。 - 早期拒绝策略 (Table 3, Figure 9, Figure 10): 对比了基线、早期拒绝和基于预测的早期拒绝三种策略,清晰地展示了早期拒绝策略能够减少资源浪费,而基于预测的早期拒绝策略则能进一步缓解负载波动并提高系统处理能力。这验证了在过载场景下,这些策略对于提高有效吞吐量的贡献。
7. 总结与思考
7.1. 结论总结
Mooncake 是一项针对 LLM 服务的创新性工作,旨在解决长上下文处理和高负载(过载)场景下的性能挑战。其核心是一个以 KVCache 为中心的解耦架构,主要贡献和结论包括:
-
解耦架构: 通过将预填充和解码阶段分离到独立的集群,并利用
GPU集群中未充分利用的CPU、DRAM和SSD资源构建一个大规模的解耦KVCache池,Mooncake实现了更灵活的资源配置和更高的KVCache复用效率。 -
KVCache-centric 调度: 核心调度器
Conductor能够智能地平衡KVCache复用、实例负载和SLO要求,通过缓存感知和热点迁移策略,显著降低TTFT。 -
长上下文优化: 引入分块流水线并行(CPP)和逐层预填充(Layer-wise Prefill),高效处理长上下文请求,同时减少网络通信和
VRAM占用。 -
过载处理: 针对
MaaS平台常见的过载问题,开发了基于预测的早期拒绝策略,有效减少了因请求被拒绝而导致的资源浪费,并稳定了系统负载。 -
卓越性能: 实验结果表明,
Mooncake在长上下文场景下,吞吐量相较vLLM最高可提升525%;在真实工作负载下,处理请求量增加了75%,同时保持了高SLO满足率,尤其在TBT方面远超基线。总而言之,
Mooncake为LLM服务提供了一个高效、稳健且适应性强的解决方案,特别适用于处理大规模、高并发且具有严格SLO要求的生产环境。
7.2. 局限性与未来工作
论文作者指出了 Mooncake 当前的局限性并提出了未来的研究方向:
- 异构加速器利用: 当前旗舰加速器在计算、内存带宽和容量之间寻求平衡,但可能在某些单一指标上并非最优。未来计划探索使用异构加速器(例如,内存导向设备,如
GDDR、LPDDR、PIM、Hybrid Bonding技术),这些设备在带宽和容量方面具有成本优势,可用于优化解码阶段的内存密集型操作。 - 更细粒度的算子解耦: 考虑将注意力算子(在解码阶段通常是内存密集型)从其他线性算子中分离,以进一步提高资源利用率。
- KVCache 优化技术集成: 结合其他
KVCache缩减技术(如压缩、重要词元选择、跨层共享)来增加批处理大小并提高KVCache命中率。 - 高级调度策略: 开发更复杂的调度策略,以处理不同请求优先级和具有不同
TTFT/TBT SLO的场景。 - KVCache 管理优化: 改进
KVCache的复制、迁移和专门的淘汰策略(例如针对部分命中和过期场景)。 - 动态资源平衡与卸载: 动态平衡预填充和解码实例的资源分配,并探索在工作负载波动时利用闲置资源进行批处理任务卸载(batch-oriented offloading tasks)。
- 请求级别预测: 探索更准确的请求级别输出长度预测,以进一步优化调度决策。
7.3. 个人启发与批判
7.3.1. 个人启发
- KVCache 的核心价值重估:
Mooncake将KVCache从一个仅仅是中间数据结构提升到系统架构和调度的核心,这提供了一个全新的视角。通过解耦KVCache存储并进行中心化管理和智能调度,极大地释放了其复用潜力和系统吞吐量。 - 解耦架构的深度思考: 论文不仅将预填充和解码阶段解耦,还深入考虑了
KVCache的物理存储解耦(利用CPU DRAM和SSD),以及通过CPP和逐层预填充在计算和传输层面的解耦,这展示了多层次解耦的强大潜力。 - 过载场景的实战价值: 多数学术研究关注理想或资源充足的情况,而
Mooncake直面MaaS平台最棘手的“过载”问题。其早期拒绝和基于预测的拒绝策略,对于保障生产系统的稳定性和有效吞吐量具有极高的实践价值。这提醒我们,在设计系统时,不仅要考虑“最佳情况”,更要考虑“最坏情况”或“常态化挑战”。 - 长上下文的系统级解决方案: 随着
LLM上下文窗口的不断扩大,长上下文推理的性能瓶颈日益突出。Mooncake提出的CPP和逐层预填充,提供了在系统层面解决这一挑战的有效途径,特别是其对网络带宽和VRAM占用的优化,具有前瞻性。 - 资源利用的精细化: 利用
GPU服务器中CPU、DRAM和SSD这些常被忽视的“闲置”资源来存储KVCache,是一种非常巧妙且成本效益高的资源利用方式。这启发我们在设计大规模系统时,应更全面地审视所有可用的异构资源。
7.3.2. 批判与潜在改进
- 预测准确性与鲁棒性: 基于预测的早期拒绝策略的有效性高度依赖于预测模型的准确性。尤其是在
LLM工作负载动态变化、用户行为不可预测的真实环境中,系统级预测(目前Mooncake采用的)可能存在局限性。未来的请求级别预测虽然困难,但若能实现,将进一步提升系统性能。预测失败可能导致不必要的拒绝或SLO违反。 - 预填充与解码实例的动态比例调整: 论文提到
Mooncake-[2P+2D]配置性能不佳是由于预填充和解码实例负载不平衡。目前系统需要预设实例比例。在动态工作负载下,如何实现预填充和解码实例数量的弹性伸缩和自动负载平衡,将是一个重要的挑战和改进方向。 - KVCache 热点迁移的策略深度: 论文中的
KVCache热点迁移采用启发式方法。虽然有效,但在更复杂的网络拓扑、更精细的负载预测以及更严格的SLO约束下,可能需要更具决策理论基础的迁移策略,例如结合强化学习或更复杂的预测模型来优化迁移时机和目标。 - 异构加速器集成的复杂性: 论文提及未来探索异构加速器。这无疑是正确的方向,但实际集成过程中,如何协调不同类型加速器(计算导向 vs. 内存导向)的调度、数据传输、编程模型和故障处理,将是一个巨大的工程挑战。
- KVCache 压缩与剪枝的深度融合: 论文提到了
KVCache压缩等算法作为未来工作。如果能将这些算法深度融入到KVCache-centric架构中,例如在KVCache Pool层实现智能的压缩/解压缩、生命周期管理和优先级剪枝,将可能带来更大的内存和带宽效率提升。 - 通用性评估: 尽管使用了多种数据集和真实工作负载,但
Kimi的工作负载具有“长上下文”的特点。Mooncake在短上下文、高RPS但低KVCache复用率的场景下,其解耦和KVCache-centric的优势是否依然显著,需要更广泛的验证。
相似论文推荐
基于向量语义检索推荐的相关论文。