LLM Serving 为什么越做越像操作系统:Continuous Batching、KV Cache 与请求调度系统拆解

发布时间:2026/6/26 2:53:14
LLM Serving 为什么越做越像操作系统:Continuous Batching、KV Cache 与请求调度系统拆解 目录为什么 LLM Serving 的难点不是“把模型跑起来”传统静态 Batch 为什么不适合在线推理Continuous Batching 到底在解决什么问题Prefill 和 Decode同一个模型两种完全不同的负载KV Cache 为什么是推理系统真正的资源中心一个可用的调度器到底要做哪些决策常见优化手段PagedAttention、Chunked Prefill、Prefix CachevLLM、SGLang、TensorRT-LLM 在工程侧分别强调什么性能分析应该盯哪些指标面试里怎么讲清楚这套系统学习路线与实践建议1. 为什么 LLM Serving 的难点不是“把模型跑起来”很多人第一次做大模型推理服务会觉得问题很直接模型加载到 GPU请求进来后做一次 forward把 token 流式返回给用户单用户 Demo 确实可以这么理解。但只要场景从“单请求能跑”变成“多用户并发上线”问题立刻就变了。在线 LLM Serving 真正要解决的是三个目标同时成立延迟要低尤其是首 token 延迟不能太高吞吐要高同样 GPU 要服务更多并发请求成本要可控显存不能被低效管理方式白白浪费这三个目标天然互相拉扯。比如你把 batch 做大吞吐可能上去但单个请求要等更久你只追求最低延迟每次来一个请求就立刻跑GPU 很容易吃不满你想支撑长上下文和大并发KV Cache 很快会把显存打满。所以 LLM Serving 的本质并不是“执行一次 Transformer”而是在有限 GPU 显存和算力下把很多长度不同、阶段不同、生命周期不同的请求组织成一套持续流动的执行系统。这也是为什么它越来越像操作系统或者数据库执行器而不是一个简单推理脚本。2. 传统静态 Batch 为什么不适合在线推理在很多 CV 或离线推理任务里静态 batch 很自然攒够一批样本一次送进模型统一计算吞吐通常更高。但 LLM 在线推理有两个根本不同点。2.1 每个请求长度不同不同用户的输入长度不同输出长度也不同有的 prompt 只有几十 token有的 prompt 是几千 token有的只生成一句话有的会持续生成几百个 token这意味着同一个 batch 中不同请求会在不同时间结束。如果还坚持“整批同步推进”就会出现典型浪费短请求已经完成但 slot 还被长请求拖着新请求来了也不能及时塞进空出来的位置GPU 执行节奏被最慢的请求绑架2.2 Decode 是逐 token 迭代的大语言模型不是一次性吐出整段答案而是先做一次 Prefill处理完整 prompt再进入 Decode每次只生成一个 token把新 token 拼回上下文继续下一轮传统静态 batch 更适合“一次大计算”不适合“很多请求在不同生命周期里滚动前进”的工作负载。如果还用静态 batch系统就会遇到两个典型问题批内等待严重尾部请求拖慢整体批次边界僵硬GPU 空闲槽位不能立刻复用这就是 Continuous Batching 出现的背景。3. Continuous Batching 到底在解决什么问题Continuous Batching 可以直译为“连续批处理”或“滚动批处理”。它的核心思想并不复杂不是等整批请求一起开始、一起结束而是在每一轮解码时动态维护当前活跃请求集合谁还活着就继续参加下一轮谁结束就立刻释放资源新请求满足条件就尽快插入。如果把传统静态 batch 看作一辆定点发车的大巴Continuous Batching 更像地铁系统有人下车位置立刻空出来新乘客达到条件就补进来系统持续流动而不是一趟车跑完再发下一趟3.1 它解决的第一个问题提高 GPU 利用率LLM Decode 阶段单步计算量并不大但轮次很多。如果每个请求单独跑GPU 很难被喂满。Continuous Batching 会把多个活跃请求在同一步合并执行让 GPU 每轮都尽量有事可做。3.2 它解决的第二个问题减少批边界带来的浪费静态 batch 中一个请求结束后剩余算力不能立即被新请求利用。Continuous Batching 则允许空出来的执行 slot 及时复用。3.3 它解决的第三个问题兼顾吞吐与延迟真正优秀的 serving 系统不是单纯追求大 batch而是让 batch 在动态变化中尽量维持“足够大但不过度等待”的平衡。这其实就是调度问题而不是单纯的模型推理问题。4. Prefill 和 Decode同一个模型两种完全不同的负载很多推理优化讨论不清楚根源在于把 Prefill 和 Decode 混成了一类工作负载。但它们在工程上几乎是两种不同任务。4.1 Prefill 更像吞吐型任务Prefill 要处理整段输入 prompt本质上是对输入序列做完整前向计算。这个阶段通常有几个特点计算密度更高更容易并行更偏 compute-bound适合做较大的矩阵计算如果 prompt 很长Prefill 往往会成为首 token 延迟的主要来源。4.2 Decode 更像时延敏感任务Decode 每一步只新增一个 token但要读取历史 KV Cache。这个阶段通常单步计算量小迭代次数多更容易受显存带宽和 KV 访问影响对调度策略极其敏感很多系统在 Decode 阶段的瓶颈根本不是“算不动”而是KV Cache 访问成本高活跃请求长度差异大调度碎片严重小步执行时 GPU 吃不满4.3 为什么很多系统会做 Prefill/Decode 解耦因为这两种负载太不一样了。把它们强行绑在一起会导致长 prompt 的 Prefill 挤占 Decode 资源时延敏感的小请求被大请求拖慢调度器难以同时兼顾 TTFT 和 TPS所以很多新系统会考虑Prefill 和 Decode 分开队列甚至分开实例、分开 GPU通过中间层传递 KV 或中间状态这类架构不是“更复杂而已”而是为了匹配两类不同的性能目标。5. KV Cache 为什么是推理系统真正的资源中心如果说训练系统的核心资源常常是参数、梯度和优化器状态那么推理系统的核心资源往往就是 KV Cache。5.1 KV Cache 的作用自回归生成时历史 token 的 Key 和 Value 没必要每一步都重算所以会把它们缓存起来。这样在下一轮 Decode 时只需要为新 token 计算 Q/K/V并复用历史 K/V。这个思想非常重要因为它把原本会反复重算的历史上下文变成了“显存换算力”。5.2 KV Cache 为什么会成为系统上限KV Cache 会随着几个因素线性膨胀batch 中活跃请求数每个请求的上下文长度生成长度模型层数hidden size / num_heads / head_dim这意味着系统并发能力经常不是先被算力打满而是先被 KV Cache 打满。也就是说很多时候你不是“算不过来”而是“放不下更多活跃会话”。5.3 KV Cache 带来的三个工程问题第一显存占用大。第二内存分配和回收频繁。第三碎片管理复杂。如果每个请求都按连续大块显存分配 KV短请求结束后会留下大量不规则空洞。请求长度一旦高度动态碎片问题会迅速恶化。这也是 PagedAttention 一类方案重要的原因它本质上是在用分页思路管理 KV Cache而不是要求每个请求都拿到一整块连续空间。6. 一个可用的调度器到底要做哪些决策很多人把“调度”理解成简单排队但在线推理里的调度器其实要连续做很多决策。6.1 准入控制新请求什么时候进入 GPU新请求到了之后不一定立刻能上 GPU。调度器要判断当前显存是否还能容纳它的 KV 预算当前 batch 是否适合插入新的 Prefill插入后会不会显著恶化在线请求延迟这本质上是 admission control。6.2 批构建本轮执行哪些请求每一轮 Decode 或 Prefill系统都要决定选哪些活跃请求进本轮是否优先让快结束的请求先完成是否给短请求更高优先级是否限制长尾请求占据过多资源不同策略会直接影响平均延迟P99 延迟吞吐用户体感公平性6.3 资源回收什么时候释放 KV请求结束、被取消、超时或被截断时KV Cache 需要及时回收。这个动作如果做得慢显存会被“逻辑上已结束、物理上未回收”的会话拖住。6.4 抢占与公平性当高优先级请求进来时要不要打断低优先级请求当长请求持续占坑时要不要限制它的步进速度当系统接近满载时要优先保吞吐还是保延迟这些问题本质都属于策略层而不是算子层。7. 常见优化手段PagedAttention、Chunked Prefill、Prefix Cache真正成熟的 LLM Serving 系统往往不是只靠 Continuous Batching而是多种机制组合。7.1 PagedAttention把 KV Cache 从“连续分配”改成“分页管理”它借鉴了操作系统的分页思想把每个请求的 KV Cache 切成固定大小的 block分配更灵活回收更细粒度能显著降低显存碎片代价是地址映射、block 管理和访存模式会更复杂但从系统角度看通常是值得的。7.2 Chunked Prefill避免超长 Prompt 一次性霸占执行资源如果某个请求 prompt 非常长一次完整 Prefill 可能把在线 Decode 请求拖住。Chunked Prefill 的思路是把长输入拆成多个 chunk 分阶段执行首 token 延迟更稳定大请求对小请求干扰更小调度器更容易穿插执行它本质上是在牺牲一点单请求极致效率换系统整体可控性。7.3 Prefix Cache复用公共前缀很多业务场景里请求前缀高度重复比如固定 system prompt多轮对话中的历史公共片段RAG 模板化提示词如果这些前缀每次都重新 Prefill会非常浪费。Prefix Cache 的目标就是把可复用的前缀计算结果缓存下来减少重复 Prefill。它对线上吞吐和首 token 延迟都有实际价值但前提是前缀复用命中率足够高缓存一致性和回收策略设计得当8. vLLM、SGLang、TensorRT-LLM 在工程侧分别强调什么这几个名字经常一起出现但它们的工程强调点并不完全相同。8.1 vLLMvLLM 被广泛关注一个核心原因就是它把PagedAttention 高效 KV Cache 管理 Continuous Batching做成了非常强的通用 serving 基座。它解决的是如何把显存利用率做高如何让动态请求混部时碎片更少如何让在线吞吐更稳定8.2 SGLangSGLang 更强调更灵活的推理编排前缀复用和结构化执行面向复杂推理工作流的调度效率如果你把它只看成“另一个推理框架”会低估它的价值。它更像是在服务引擎之上继续优化推理时的程序组织方式。8.3 TensorRT-LLMTensorRT-LLM 更偏底层性能压榨常见关注点包括kernel fusion图优化更高效的执行计划对特定硬件栈的深度适配它通常在单机单卡或固定部署环境下有很强性能优势但工程落地时仍然需要和上层调度、KV 管理、请求编排能力配合。8.4 一个常见误区很多人做技术选型时会问“vLLM 和 TensorRT-LLM 谁更快”。这个问题本身就有点粗。更准确的问题应该是你的瓶颈主要在算子执行还是在在线调度你的业务更看重极致吞吐还是灵活多租户服务你的请求分布是长 prompt 为主还是短请求高并发你的系统更缺 kernel 优化还是更缺 KV 和调度能力否则很容易把“执行器性能”和“系统级吞吐”混为一谈。9. 性能分析应该盯哪些指标如果你在做 LLM Serving只看一个“tokens/s”远远不够。至少要把指标拆成几层。9.1 用户体验指标TTFTTime To First Token首 token 延迟TPOTTime Per Output Token单 token 输出间隔E2E Latency端到端延迟P50 / P95 / P99 延迟9.2 系统吞吐指标Requests/sOutput tokens/sTotal tokens/s平均并发活跃请求数9.3 资源指标GPU 利用率显存占用率KV Cache 占用与命中率batch size 随时间变化Prefill / Decode 各自占比9.4 调度健康度指标请求排队时间每轮 batch 构建耗时被拒绝或降级的请求数KV block 碎片率长请求对短请求的拖累程度如果这些指标你都看不到其实系统还是黑盒优化很难做扎实。10. 面试里怎么讲清楚这套系统这一类问题在 AI Infra、LLM Serving、推理优化岗位里非常常见。面试官通常不是只想听名词而是想看你有没有系统观。你至少要能讲清三层逻辑。10.1 第一层为什么静态 batch 不够核心回答在线请求长度高度动态Decode 是逐 token 迭代静态 batch 会造成严重等待与资源浪费10.2 第二层Continuous Batching 的本质是什么核心回答动态维护活跃请求集合请求结束就释放 slot新请求按策略插入目标是在吞吐、延迟和公平性之间找平衡10.3 第三层真正的瓶颈为什么常常落在 KV Cache 和调度核心回答并发上限经常受 KV 显存约束Decode 更容易受带宽与访存影响调度策略决定 GPU 是否能持续高效工作如果还能进一步聊到 PagedAttention、Chunked Prefill、Prefix Cache、PD 分离等就已经比较像真正做过这类系统的人。11. 学习路线与实践建议如果你想把这个方向学扎实我建议按下面顺序走。11.1 先把 Transformer 推理路径吃透至少搞明白Prefill 和 Decode 的计算区别Attention 在推理阶段如何使用 KV Cache为什么长上下文会让显存压力快速增长11.2 再看一个真实 serving 引擎建议至少读一套vLLM 的调度和 KV 管理思路SGLang 的执行编排思路TensorRT-LLM 的执行优化路径重点不要停留在“会用命令启动”而是要追问它的 batch 是怎么滚动维护的它的 KV 是怎么分配和回收的它如何平衡 TTFT 和吞吐11.3 最后做自己的最小原型哪怕不写完整引擎也可以自己做一个简化版模拟器输入不同长度请求流设计一个简单调度器模拟静态 batch 和 continuous batching统计平均延迟、吞吐、资源利用率很多人看了很多概念真正一做模拟才发现调度策略会直接改变系统形态平均值和尾延迟经常不是一个方向看似简单的“插队”会影响公平性这类实验特别适合写进简历或面试项目里因为它能体现你不是只会调用框架而是理解系统本质。总结LLM Serving 的关键难点从来都不只是“模型推理得够快”而是如何把多请求、多阶段、长生命周期的生成任务在 GPU 上组织成一个持续高效运转的系统。从这个角度看Continuous Batching 解决的是动态请求集合的执行效率问题KV Cache 管理决定了并发上限和显存利用率PagedAttention、Chunked Prefill、Prefix Cache 都是在补系统层短板vLLM、SGLang、TensorRT-LLM 的差异本质上也是不同层面的优化侧重如果你未来想走 AI Infra、推理优化、LLM Serving、GPU 系统工程这条路线这一层能力非常值得深挖。因为它连接了模型结构、GPU 执行、显存管理、调度策略和线上服务成本是一个非常典型的“算法与系统交界地带”。很多岗位真正想要的不是只会讲 Transformer 原理的人也不是只会调 API 的人而是能把“模型如何生成一个 token”一路讲到“线上系统如何稳定服务千万次生成请求”的工程师。