发表时间: 2025-11 · arXiv:2511.02230 (UC Berkeley)
Continuum:利用KV缓存生命周期实现高效、鲁棒的多轮LLM智能体调度
Hanchen Li∗1, Runyuan He∗1, Qiuyang Mang1, Qizheng Zhang2, Huanzhi Mao1, Xiaokun Chen3, Hangrui Zhou4, Alvin Cheung1, Joseph Gonzalez1, Ion Stoica1
1加州大学伯克利分校 2斯坦福大学 3Tensormesh 4清华大学
核心问题:现有的大语言模型(LLM)推理引擎为了最大化利用率,普遍采用“轮次结束即驱逐”(end-of-turn eviction)的KV缓存管理策略。这意味着一旦请求的解码完成,其KV缓存就会被丢弃,以便为等待队列中的新请求腾出空间。该策略虽然适用于多轮聊天应用,但对于现代智能体(agentic)工作负载却会显著降低性能。智能体工作负载的特点是LLM调用与工具调用交错进行,工具调用会引入暂停,而现有策略会在此期间驱逐KV缓存,导致智能体返回继续推理时无法有效复用。
研究目标:本文旨在解决智能体工作负载中因轮次结束驱逐策略引发的两个核心性能瓶颈:
1. 重复的重计算/重加载开销:当工具调用结束后,如果KV缓存已被驱逐,系统必须重新计算前缀(prefill)或从CPU内存重加载(若启用了offloading),这会引入显著的延迟。
2. 累积的每轮排队延迟:更严重的是,即使启用了CPU offloading以复用KV缓存,被驱逐的请求在返回时也必须重新进入等待队列,等待其他正在运行的请求释放GPU内存后才能开始推理。这种排队延迟会在智能体的多个轮次中不断累积,导致总延迟随轮次增加而增长(如图1所示)。
创新点:为解决上述问题,本文提出了Continuum,一个为多轮智能体工作负载设计的服务系统,其核心创新在于引入了KV缓存的“生命周期”(Time-to-Live, TTL)机制。
* 基于成本效益模型的TTL决策:对于生成工具调用的请求,Continuum会选择性地将其KV缓存固定(pin)在GPU内存中,并为其分配一个TTL值。该TTL值的确定基于一个效用模型,该模型同时考虑了保留缓存所能避免的重计算/重加载成本,以及更关键的——减少的每轮排队延迟。同时,模型也权衡了在TTL期间占用GPU内存的机会成本。
* 鲁棒的缓存管理:当TTL到期后,KV缓存可以被自动驱逐以释放GPU内存。这种机制为系统提供了鲁棒性,即使在工具调用时长预测不准或出现异常长时间调用的情况下,也能防止GPU内存被无限期占用,避免了系统死锁。
* 结合程序级FCFS的调度:Continuum将TTL机制与程序级优先的先到先服务(Program-level First-Come-First-Serve)调度策略相结合。这有助于保持多轮智能体程序的执行连续性,优先处理已执行多轮、接近完成的程序,从而简化了复杂智能体工作流的调度。
* 显著的性能提升:在真实的智能体工作负载(如SWE-Bench, BFCL, OpenHand)和多种模型(Llama-3.1 8B/70B, Gemma-3 12B, GLM-4.5 355B)上的评估表明,Continuum能够将平均作业完成时间(JCT)提升超过8倍,同时提高了系统吞吐量。
图1: 先前智能体服务系统的两种主要失败模式。红色块表示次优调度和KV缓存管理带来的开销:即使有CPU offloading,智能体在KV缓存被驱逐后仍会遭受排队延迟。
ReAct循环已成为标准:大多数现代智能体工作负载遵循ReAct范式【索引79, Shunyu Yao et al. React: Synergizing reasoning and acting in language models. 2022. The eleventh international conference on learning representations】,该范式在推理步骤(LLM解释上下文并输出思考)和行动步骤(调用外部工具)之间交替进行。这一范式因其清晰和高效已成为事实上的标准,被编码智能体(如Claude Code【索引8】、Cursor【索引19】)、开发框架(如LangChain【索引42】、LangGraph【索引43】)广泛采用,并且最近的开源模型(如GPT-OSS【索引3】、Kimi-K2【索引1】)也直接将工具调用能力集成到基础模型中。
长序列、多轮迭代成为趋势:一个重要趋势是,智能体应用越来越多地将此循环扩展到长时程、多轮的迭代中,在数十甚至数百个轮次中反复交织思考、工具调用和上下文更新。这一点在近期的基准测试中得到了体现,例如用于工具-智能体-用户交互的τ-bench【索引78】,用于多轮工具增强交互的MINT【索引69】,以及用于多轮决策和工具使用场景的AgentBench【索引48】。
先前的工作由于以下三个主要原因,无法有效处理这种新兴的复杂工作负载:
图2: 一个SWE-Agent的说明性示例。该智能体通过中间的工具调用逐步解决一个软件工程错误。这些工具调用的持续时间不同,并中断了LLM推理的连续性。
未考虑工具调用的特性:Autellix【索引51】引入了程序级已达服务(PLAS)调度,优先处理累积服务时间较少的智能体程序请求。Tempo【索引84】提出了一种调度器,以在面对不同类型请求(聊天、智能体、推理)时满足服务等级目标(SLO),而本文则专注于具有多轮和可变工具调用的智能体工作负载。这些工作未能考虑智能体工作负载中工具调用的独有特性,例如其可变的持续时间以及对KV缓存管理的影响。这种疏忽会导致次优的调度决策和增加的延迟,正如稍后在3.2节中展示的。
KV缓存保留策略不足:一些先前的工作注意到了智能体工作负载中KV缓存复用的挑战。InferCept【索引2】引入了一种“保留”(preserve)操作,在工具调用之间固定KV缓存。然而,其策略忽略了请求的多轮性质。当KV缓存在轮次之间被驱逐时,程序返回时会产生额外的每轮排队时间。在多轮场景中,排队时间会逐轮累积。忽略这种效应使得它们即使在有显著收益时也不会在GPU中保留KV缓存。此外,它们的保留操作是固定的,无法实时适应工具使用情况。如果实际工具调用时间远长于预期,盲目“保留”KV缓存会导致严重的效率低下,这使其不适用于实际部署。Pie【索引25】引入了一个可编程服务系统,将生成循环分解为细粒度的处理器,并将控制权委托给用户程序,允许自定义工具调用处理。然而,它要求开发者为每个智能体手动设计调度策略,并且没有提供适应动态工具调用延迟或多轮依赖关系的实际方法。
表1: Continuum与代表性基线的比较。
图3: Sec 6中使用的智能体工作负载SWEBench和BFCL的特性。随着步骤数的增加,请求更接近完成。
对真实轨迹的分析:我们首先分析现代智能体工作负载的特性。我们收集并分析了100个来自在SWE-Bench【索引33】上运行的mini-swe-agent【索引45】的轨迹,以及100个来自运行GPT-5作为基础模型的BFCL V4 Web Search【索引53】的轨迹。图2展示了一个来自SWE-Bench的简化示例轨迹,说明了智能体如何逐步解决一个软件工程任务。
三个关键发现:分析的结论有三点。首先,这些新型智能体程序包含许多轮次,这增加了调度难度。其次,工具调用的时间分布各异,但许多调用是短暂的。虽然在生成这些短暂的工具调用后请求会被视为完成,但下一个请求会在工具调用完成后很快到达,从而可以复用KV缓存。
剩余工作量随轮次减少:最后,如图3所示,对于SWEBench和BFCL这两种工作负载,随着程序接近完成,未来预期的总token数量会减少。这表明后续轮次的预期完成时间更短。这提示我们,优先处理较早到达的请求(程序级FCFS)或已执行更多轮次的请求,可能是对理论上最优但需要预知未来的“最短剩余时间优先”(SRTF)调度策略的一个良好近似。但是,当涉及工具调用时,维持这种顺序并非易事,我们将在3.2节中讨论。
表2: 从两个收集的数据集中得到的统计数据。报告的数字格式为(均值,标准差)。
图4: CPU offloading下的每个程序的排队延迟。InferCept的保留决策忽略了排队成本,因此被驱逐的程序在各轮次中仍然累积了大量的等待时间——尽管InferCept节省了重载成本,但其等待时间与原始vLLM相当。
基于轮次的驱逐问题:尽管这些工具调用可能很短,但推理引擎将它们视为LLM请求之间的同质间隙。vLLM或SGLang会在解码一完成就驱逐请求的KV缓存,这隐含地假设请求已完成。然而,如果KV缓存被驱逐,当启用offloading时,引擎必须重新进行完整的prefill或从DRAM重载KV缓存,从而产生额外延迟。大多数系统在高效处理这些场景方面都存在不足。图1说明了这种效应:工具调用创建了一个暂停,触发了KV缓存驱逐,导致返回时需要prefill或KV重载。因此,制定一个考虑工具调用的KV缓存保留策略以避免此类开销至关重要。
每轮排队延迟的累积:智能体程序的多轮性质也给调度器带来了先前工作严重忽视的新挑战。当当前智能体程序等待工具调用时,如果调度器为了最大化吞吐量而将GPU内存分配给其他请求,当前程序的KV缓存将从GPU内存中被移除。当该程序的工具调用返回并将后续LLM请求发送给调度器时,它必须在其他请求的prefill/decoding之后等待,直到有空闲的GPU空间。这个等待周期在智能体程序的执行中产生了一个间隙,无论KV缓存是否存储在CPU DRAM中。如图1所示,除了之前的prefill/加载成本外,这个间隙也导致了由工具调用引起的延迟,并在多轮中累积,给每个程序带来巨大延迟。此外,它还破坏了程序执行的连续性,并使得较早到达的请求被安排在较晚到达的请求之后执行。需要注意的是,即使我们在等待队列中给予新请求最高优先级,它仍然会被GPU上已在运行的其他请求的计算所阻塞。
现有工作对排队延迟的忽视:现有工作在其保留策略中没有考虑每轮排队延迟。InferCept【索引2】的KV“保留”操作仅在CPU offloading成本超过工具调用期间的预估GPU占用成本时才被调用。关键是,这个决策只考虑了紧接着下一轮的重载成本——它完全忽略了被驱逐的程序在重新进入等待队列、排在其他活动请求之后时将经历的排队延迟。随着像LMCache【索引15】这样的引擎提供了快速的异步CPU offloading,重载成本变得很小,因此InferCept的保留操作很少被调用。然而,无论offloading速度多快,排队延迟始终存在:即使KV可以即时重载,返回的请求仍然必须等待被其他请求占用的GPU内存被释放。由于这个排队成本在每一轮都会产生,总累积延迟与每个程序的轮次成正比——这恰恰是智能体工作负载运行的场景。我们在图4中展示了由于缺乏对多轮调度的考虑而导致的性能下降。我们分析了vanilla vLLM和InferCept算法中每个请求经历的总驱逐开销。x轴表示按到达时间顺序排列的每个智能体程序,y轴表示每个智能体作业的总“气泡时间”——即请求在执行前在等待队列中经历的总空闲时间。即使有InferCept的KV保留,气泡仍然存在并导致延迟增加,尽管其吞吐量比vLLM有所提高。
图5: 函数的执行时间可能呈极端的长尾分布。最慢的10%的fetch_url调用占总延迟的52.5%,而最慢的10%的cd调用占总延迟的94.1%。
工具调用的可变性:当前的KV缓存保留策略在工具调用时间变化很大时也会失效。例如,InferCept的方法在工具调用后将KV缓存固定在GPU内存中,直到下一个请求到达。这种方法在工具调用延迟稳定时工作良好。然而,如图5所示,许多工具调用的执行时间表现出高度的可变性。当工具调用花费的时间远超预期时,被固定的KV缓存可能会长时间占用GPU内存。在数据库智能体中也观察到类似的模式,因为外部工具调用更为复杂。这导致内存使用效率低下,甚至在保留的KV缓存完全占用GPU时可能导致死锁。因此,静态保留策略在实际场景中缺乏鲁棒性。
图6: 需要精心设置生命周期(time-to-live),以在内存使用与prefill加每轮排队延迟之间取得平衡。
鉴于先前工作的失败,我们确定了服务智能体工作负载的关键问题:如何在多轮场景中高效且鲁棒地保留KV缓存?
一个最优的KV缓存保留策略应具备以下特点:
* 它应为那些在工具调用后很快会复用KV缓存的请求保留缓存,以最小化prefill/加载开销。
* 它应考虑智能体程序的多轮连续性,减少等待时间并保持程序顺序。
* 它应对可变的工具调用延迟具有鲁棒性。
为了实现鲁棒性保证,我们建议借鉴传统系统中的“生命周期”(Time-to-live, TTL)思想:为每个请求的KV缓存赋予一个TTL值,定义其在GPU内存中保留的最长持续时间。这可以防止长时间运行或失败的工具调用无限期地阻塞GPU资源,同时又能保留KV缓存。
然而,与静态保留操作相比,为每个KV缓存条目设置合适的TTL值具有挑战性。首先,TTL值不应过大。如图6所示,如果超时持续时间太长,被固定的KV缓存会不必要地占用GPU内存,阻塞其他请求并降低整体系统吞吐量。另一方面,如果特定KV缓存的固定时间太短,KV缓存会在工具调用完成前被驱逐,尽管浪费了GPU占用时间,但仍会导致昂贵的重计算或调度气泡。
考虑到这些权衡,TTL值应被仔细设置。只有当我们能基于工具调用持续时间、prefill/加载成本以及对程序连续性的度量来设置合适的TTL值,我们才能在缓存复用的好处与为其他请求维持系统吞吞吐量的需求之间取得平衡,从而实现良好性能。
算法1 Continuum调度
1: 函数 OnRequestArrival(request r):
2: Q ← Q ∪ {r}, id ← r的程序ID;
3: 如果id是已见过的程序:
4: (f, t) ← r中的工具调用信息;
5: S[f] ← S[f] ∪ {t};
6: 函数 OnRequestFinish(request r):
7: 如果r是其程序的最后一个请求:
8: 释放r使用的KV缓存;
9: 否则:
10: f ← 完成r后要调用的下一个工具;
11: id ← r的程序ID;
12: P[id] ← CalcTTL(r, S[f]);
13: 函数 Schedule():
14: 当Q不为空时:
15: 对于P.keys中的每个id:
16: 如果 当前时间 > P[id] 并且 id ∉ Q.programs:
17: 释放id上一个请求使用的KV缓存;
18: P ← P \ (id, P[id]);
19: r ← argmax_{r'∈Q} CalcPriority(r', P);
20: 如果r无法放入内存:
21: break;
22: 否则:
23: Q ← Q \ {r};
24: 将r发布到运行状态;
25: id ← r的程序ID;
26: 如果 id ∈ P.keys: P ← P \ (id, P[id]);
全局状态:等待队列Q;TTL映射P(记录被固定的程序及其TTL);历史工具调用记录S,其中S[f]表示工具f的记录信息
表3: Continuum成本模型中针对请求r及其关联工具调用f的关键符号。
建立成本效益模型:为了给一个请求r的KV缓存固定设置一个有效的TTL值(单位:秒),Continuum必须选择一个能最好地平衡潜在复用收益与其成本的值。收益和成本都以时间为单位衡量,因为它们最终都转化为所有程序总作业完成延迟的变化。数学上,给定一个请求r和一个TTL值τ,Continuum估算为请求r的KV缓存固定τ时间的成本Cost(τ, r)和收益Benefit(r)。为简单起见,Benefit(r)假设下一个请求在TTL窗口内到达。TTL在工具调用返回前过期的情况在4.2节中讨论。
成本估算:固定一个请求的KV缓存的成本来自于占用GPU内存的机会成本,这部分内存本可以用来服务其他请求:
其中MemUsage(r)是请求r的KV缓存使用的GPU内存量,M是活动请求的平均GPU内存占用,τ是TTL值。比率MemUsage(r)/M表示当r被固定时,有多少个平均请求被阻塞。换句话说,如果固定r占用的内存与k个请求相同,那么固定r会给大约k个其他请求增加τ的延迟。我们假设当需要保留KV缓存时,等待队列中总有足够的请求使这种阻塞效应发生。
收益估算:固定一个请求的KV缓存的收益在请求于TTL周期内被重新发出时实现,使其能够避免重载或预填充r程序KV缓存的开销,同时节省每轮的排队延迟:
Benefit(r) = CacheMissCost(r) + OutofOrderCost(r)
这里,CacheMissCost(r)衡量为请求r重载或预填充KV缓存的成本,而OutofOrderCost(r)衡量由于等待其他请求释放GPU内存而导致的预期排队延迟。我们使用所避免的成本总和作为收益。
缓存未命中成本的量化:与Cost(τ, r)类似,我们可以通过以下两方面来衡量CacheMissCost(r):(1) 上下文重建开销Prefill-Reload(r);(2) 将经历额外延迟开销的大约请求数量MemUsage(r)/M。该成本正式定义如下:
Prefill-Reload(r)是prefill或重载的时间成本,取决于是否开启了CPU offloading。这基于5.2节中描述的快速离线剖析。
衡量预期排队延迟:如3.2节所讨论,保留KV缓存也消除了返回程序在被驱逐后会经历的排队延迟——即使CPU offloading使得重载本身很快。这个OutofOrderCost部分是先前保留策略(如InferCept【索引2, Reyna Abhyankar et al. Infercept: Efficient intercept support for augmented large language model inference. 2024. ICML】)中缺失的关键项,它们只考虑了重载成本。通过对该项建模,Continuum可以证明即使重载成本很低,只要排队延迟的节省超过了GPU内存占用成本,保留KV缓存也是合理的。请注意,排队延迟的收益与工作负载的“记忆性”(memoryfulness)密切相关,即剩余步骤数是否随着程序的进展而可预测地减少。
记忆性因子η的引入:例如,如果每个程序发出的请求数遵循几何分布,那么无论已服务多少请求,预期的剩余请求数都是恒定的;在这种情况下,固定缓存对排队延迟没有好处,因为保持顺序并不能加速优先完成短作业。相反,如果每个程序发出固定数量的请求,那么TTL可以通过近似“最短作业优先”来消除排队成本。让N为一个程序中的总请求数,k为已服务的请求数。我们定义以下记忆性因子η:
我们可以看到这个因子很好地模拟了工作负载的记忆性程度:当工作负载完全无记忆时,我们有k与N-k无关,导致η = 0。相反,当工作负载完全有记忆,即所有程序都有相同固定数量的请求时,我们有Corr(k, N-k) = Corr(k, -k) = -1,导致η = 1。注意,在某些情况下η可能小于零(极长尾的轮次分布),这表示一种反记忆模式,即在一个程序上取得进展似乎会揭示更多剩余工作。我们没有观察到这种模式,但Continuum的设计考虑了这种极端工作负载:在这种情况下,最好是短暂地服务每个程序并频繁切换,以适应长尾轮次分布。
乱序成本的定义:现在,我们准备根据上述η来定义OutofOrderCost(r)。当η = 1时,延迟恰好是r的程序返回等待队列时的等待时间。为了匹配这一点,我们记录该工作负载中历史请求每单位上下文大小的平均等待时间为T/M,其中T是先前请求的平均排队延迟。在这种情况下,延迟可以很好地用T * MemUsage(r)来衡量。这里,我们考虑MemUsage(r),因为大上下文请求更难调度(它们必须等待足够的连续内存被释放)。对于一般情况,我们将乱序成本定义如下:
确定最优TTL:在本部分,我们描述Continuum如何基于上述成本-收益模型和历史工具调用信息来设置KV缓存的TTL值。如算法1(第12行)所示,Continuum确定最优TTL值τ*以最大化保留KV缓存的预期净收益:
其中P(τ, f)估算工具调用f在时间τ内完成的概率。该公式捕捉了将请求r的KV缓存保留τ时长所带来的预期净收益(以总作业延迟衡量)。通过消去共享的MemUsage(r)/M,上述公式可以转化为:
这表明我们在实现中只需要额外计算T和P(τ, f)。T可以估算为被驱逐请求所经历的排队延迟的滑动窗口平均值。由于我们无法完全预测下一个工具调用的持续时间,我们使用从历史工具调用记录S[f]中得出的经验累积分布函数(CDF)来估算P(τ, f)。具体来说,我们计算如下:
其中I[·]是指示函数。最后,我们通过枚举S[f]中记录的所有唯一工具调用持续时间作为候选τ(包括τ=0)来求解方程(2),并选择具有最高预期回报的那个。
冷启动处理:当S[f]中的历史记录数量很少时,经验CDF估算可能不可靠。在这种情况下,我们首先尝试使用全局工具调用信息来估算P(τ, f_any),其计算方式为∑_{t∈S} I[t ≤ τ]/|S|。此外,在引擎服务刚开始时,即使是全局记录也可能不可靠。为了解决这个问题,我们设计了一个Continuum的最小版本,它使用一个固定的TTL阈值T_default,该阈值通过假设工具调用持续时间遵循单位均值的指数分布(即ToolCallDuration ∼ Exp(1))并且工作负载是完全有记忆的(即η=1)的相同成本模型推导得出。T_default被设置为此场景下的最优τ*。在实践中,我们设置一个阈值K来决定是使用固定TTL、全局记录还是基于S[f]的细粒度估算。也就是说,当|S| ≤ K时我们使用T_default;否则,当|S[f]| ≤ K时我们使用全局记录,其余情况使用细粒度TTL设置。在我们的实现中,我们设置K=100并初始化T为零。此外,由于智能体通常在投入生产前会用工具进行后训练【索引12, 14, 50】,用户也可以在训练期间获得这些成本模型的统计数据。
定义TTL感知的优先级:为了使调度与TTL算法兼容,我们需要重新定义推理引擎中的请求优先级。Continuum引入了一种TTL感知的优先级,它提升了在TTL内的被固定请求的优先级以保持连续性,同时仍然保留程序级的FCFS顺序。具体来说,调度器为等待队列Q中的每个请求r分配一个多键优先级元组,并按以下标准(顺序)对请求进行排序:
1. 被抢占状态:与原始引擎相同,被抢占的请求(由于运行队列竞争)优先于未被抢占的请求。
2. TTL状态:在其他请求中,在TTL窗口内被保留的请求优先于未被固定的请求。
3. 程序级到达顺序:最后,在每个类别内,请求按其程序级的到达时间排序,以维持FCFS的公平性。
图7: Continuum系统概览
模块化架构设计:在Continuum中,我们的设计目标是一个模块化架构,它对核心推理引擎调度器循环的改动最小。在客户端,我们为每个推理请求附加一个程序标识符(program_id),以便系统能够识别多轮智能体程序并跨步骤推理工具调用。
系统工作流程:请求到达服务引擎后,进入现有的调度器循环。Continuum增加了一个轻量的工具调用处理器(Tool-Call Handler),该处理器在请求到达和完成时被调用。处理器从LLM输出中解析工具调用,使用在同一program_id内观察到的请求间间隔来跟踪每个工具的延迟,并向调度器返回TTL。调度器使用这个提示来固定请求的KV缓存,以供下一步潜在复用,并在TTL值到期或程序终止时解除固定。
解耦的处理器设计:工具调用处理器是一个独立的类,由主调度器在请求到达或完成时调用。这种解耦结构确保了工具处理逻辑与核心调度循环保持隔离,保证了未来对解析器或工具感知策略的扩展性。
识别工具调用:当调度器完成一个请求时,它将响应转发给工具调用处理器,后者确定响应是否包含工具调用。处理器根据函数调用模式解析消息,因为LLM输出频繁采用标准化的工具调用结构,如OpenAI模式:
{ "id": " fc_0 ", "call_id ": " call_0 ", "type ": " function_call ", "name ": " get_weather ", "arguments ": {" location ": " Paris "} }
对于这个示例模式,处理器检查每个返回消息块的类型;如果它指示一个函数/工具调用,处理器提取调用的名称并将其用作工具调用类型。在SWE-Bench中,保证每个包含函数调用的LLM响应将只包含一个bash函数调用。我们提取bash块内的字符串,并使用其后的第一个单词作为工具调用名称。更多不同LLM的函数调用格式示例【索引47, 60】可在附录B中找到。Continuum可以轻松扩展以支持这些格式,只需一个类似于附录A的解析器。
记录工具完成时间:对于一个由程序ID p标识的程序中的每个LLM请求i,当调度器记录一个带有工具调用输出的已完成请求时,处理器会记录一个服务器端的完成时间戳t_{p,i}^{finish}以及工具调用名称。当具有相同p的下一个请求i+1到达时,我们观察其服务器端到达时间戳t_{p,i+1}^{arrive}并计算请求间间隔t_{p,i+1}^{arrive} - t_{p,i}^{finish}。我们将此间隔记录为这次工具调用的执行时间,以存储用于未来的TTL计算。
在工具调用处理器给出TTL值后,调度器需要执行固定(pin)操作。
请求固定:如果该步骤未被标记为最后一步(例如,被解析为包含一个工具调用),调度器调用工具调用处理器以获取TTL值τ*,如果该值不为零,则调用pin_request(request, τ*)。这会在一个字典pinned_requests中记录一个请求及其过期时间current_timestamp + τ*的键值对,并有意跳过释放该请求的KV块。pinned_requests也将被传递到等待队列,以便优先调度同一程序中的下一个请求。
请求解固定:在每个调度步骤开始时,调度器运行unpin_requests()。它扫描pinned_requests并解固定那些TTL已到期且其program_id当前未出现在等待队列中的条目。这可以防止在后续请求已到达推理引擎但调度器尚未能调度它时过早驱逐。此外,当一个程序的最后一步完成时,调度器会主动解固定任何具有相同program_id的剩余固定项,因为预计在不久的将来不会有KV缓存复用。
死锁预防:被固定的请求可能会累积,当所有GPU内存都被固定请求占用时,可能会发生死锁。由于如果同一程序的下一个请求仍在等待队列中,被固定的请求将被保留,整个调度循环可能会被卡住,由于空间不足而无法调度新请求运行。因此,我们需要一种机制在发生此类死锁时解固定请求。在Continuum中,当调度逻辑因空间竞争而无法调度新请求时,它会检查pinned_requests中是否有任何固定请求。如果有,我们迭代地从pinned_requests中选择具有最晚程序到达时间的受害者来解固定并释放空间,直到第一个请求可以被调度运行。被选择的请求将从其队列中移除,其KV缓存被释放,并根据需要重新排队,确保后续分配可以继续运行。这可以防止即使存在许多固定项时也能避免死锁。
离线剖析:为了根据上下文大小预测prefill时间和重载时间(Prefill-Reload(r)),如4.1节所需,我们为每个硬件和模型对进行离线剖析以供在线估算。我们为两个目的进行剖析:(1) CPU offloading情况下的GPU-CPU带宽。我们通过取平均CPU offloading吞吐量来测量。(2) 用于估算prefill成本的prefill与上下文长度曲线。我们通过对块大小{1000, 2000, 4000, ..., max_context_length}进行prefill并对数据拟合二次曲线来测量。诚然,请求的某些页面可能仍留在GPU内存中,不需要重新计算。但是当内存紧张时,这些剩余页面通常很小,我们通过完整的prefill时间来近似,误差很小。每个硬件模型对的剖析时间不到10分钟。
基于vLLM的实现:我们在vLLM之上实现了Continuum,大约增加了1k行Python代码。除了上述添加到调度器类中的固定操作外,我们在vLLM的原始调度器中使用了来自工具调用处理器的三个函数:
* func_call_finish(tool, timestamp): 当请求完成并被解析为包含工具调用时,此函数通知工具调用处理器记录工具调用开始时间。
* update_tool_call_time(program_id, timestamp): 当一个新请求到达时,它表示前一个请求的工具调用已完成,因此我们记录时间。
* set_up_ttl(request, tool): 基于先前的工具调用信息和系统设置,为调度器提供此已完成请求的最佳TTL值。
模型与硬件:
数据集:
主要基线系统:
核心结论:
端到端实验 (6.2)
* 在SWE-Bench、BFCL和OpenHands工作负载的轨迹回放实验中,Continuum在平均响应时间和吞吐量上都取得了显著提升。例如,使用Llama-3.1-8B模型,Continuum相比vanilla vLLM,平均响应时间减少高达2倍(图8)。
* 在启用CPU offloading时,Continuum同样表现出色,优于像InferCept这样具有智能DRAM offloading逻辑的系统,证明了其在减少调度气泡方面的正交增益(图10)。
* 对于具有更高平均轮次的OpenHands智能体,Continuum的改进更为显著,因为基线系统在高轮次下性能恶化严重(图9)。
* Continuum还能有效降低P90和P95延迟,这归功于其减少了每轮排队延迟的能力(图11)。
图8: Continuum在不同模型大小、硬件配置和数据集上均优于基线调度器。
图9: 在H100上,Continuum在使用Llama-8B运行OpenHands时,在平均延迟和P95延迟方面均取得了最佳性能。
图10: 当启用DRAM offloading时,Continuum实现了一致的改进。它通过同时考虑工具调用和多轮特性,优于像InferCept这样具有智能DRAM offloading逻辑的系统。
图11: 在使用Llama-8B模型运行SWE Bench轨迹时,Continuum实现了更好的P90和P95延迟。
真实分布式环境下的SWE-Agent (6.2)
* 在Tensormesh的H100测试平台上,对真实的SWE-agent运行500个任务进行评估。结果显示,在相同的任务通过率下,Continuum的平均延迟始终优于基线(图12)。
* 值得注意的是,Continuum的通过率实际上高于基线,因为基线系统运行时间过长(超过15分钟)会被SWE-Bench的环境超时机制判定为失败,而Continuum的效率避免了这种情况。
图12: 在分布式设置中,Continuum在真实SWE-agents的通过率下改善了延迟。
敏感性分析 (6.3)
* 推理引擎配置:Continuum的性能提升对于不同的最大批处理大小和块大小配置是稳定的(图13)。
* 轮次数量:随着轮次数量的增加,基线方法的性能下降,而Continuum保持稳定、低延迟的性能,显示了其对复杂、多轮智能体交互的有效性(图14)。
* SSD Offloading:当存储层扩展到SSD时,Continuum同样能在不同大小的磁盘上持续改善平均延迟(图15)。
图13: Continuum在不同的最大批处理大小和块大小配置下均能改善延迟。
图14: 随着轮次数的增加,Continuum显示出更高的改进,而延迟时间保持稳定。
图15: 当我们将offloading设备扩展到CPU offloading之外的SSD时,Continuum减少了延迟。
消融研究与微基准测试 (6.4)
* 消融研究:通过逐步添加Continuum的组件(程序级FCFS、静态TTL),性能逐渐提升,证明了每个设计思想的贡献(图16)。
* 调度器开销:Continuum引入的调度开销非常小(毫秒级),与LLM推理的GPU执行时间相比可以忽略不计(表4)。
* 强化学习应用:在为OpenHands Agent生成rollout的微基准测试中,Continuum在单节点上实现了比同期RL工作ThunderAgent【索引36】更高的推理步数/分钟(表5)。
图16: 各个思想对Continuum的贡献。程序级FCFS优先处理程序到达时间更早的请求。静态TTL使用从冷启动处理机制计算出的固定TTL阈值。
表4: 在不同DRAM offloading设置下,Continuum引入的调度延迟开销很小。
表5: Continuum在OpenHands rollout上比同期工作取得了更好的性能。
LLM推理系统:已有大量研究致力于改进LLM推理。vLLM【索引40】和SGLang【索引85】等服务引擎通过采用PagedAttention设计和优化内核实现了业界领先的推理性能。除了广泛的内核级优化【索引20, 80, 87】外,研究人员还在资源管理上提出了许多优化: