Skip to content
View original post on X: meng shao
Published
38/100

UCSD CSE 291A lecture 13 covers prefix caching and P–D disaggregation

AIUCSD CSE 291A Lecture 13, taught by Yiying Zhang, covers prefix caching and Prefill–Decode disaggregation for distributed LLM serving. The main reading is Preble (ICLR 2025), whose global E2 scheduler and local fair scheduler report 1.5–14.5x lower average latency than SGLang in the lecture's own results.
meng shaoVerified on X
@shao__meng
View on X

https://x.com/i/article/2119948219887341568

UCSD CSE 291A 第 13 讲:Prefix Caching 与 Prefill–Decode 解耦,分布式 LLM 推理怎样少做重复计算

第 13 讲:Distributed LLM Serving I Prefix Caching and P–D Disaggregation

加州大学圣地亚哥分校 CSE 291A · Systems for LLMs and AI Agents(Fall 2026,教师 Yiying Zhang)在 2026 年 11 月 10 日的 Lecture 13 里,专注一件事:单机、混批推理已经不够时,怎样用前缀缓存和 Prefill–Decode 解耦,在分布式 GPU 上少做重复计算,也少让两个阶段互相拖累。讲义副标题写得很直白:Prefix Caching and P–D Disaggregation。按课表,这是「分布式 LLM Serving」两讲中的第一讲;第二讲在 11 月 12 日,才会转到长上下文和 MoE 服务。

这门课不做 agent 框架入门,是大模型和 agent 的后端系统:推理、KV 缓存、调度、隔离和评测。这一讲的主阅读是 Preble(ICLR 2025);附加阅读点名 DistServe、Mooncake、NVIDIA Dynamo、POD-Attention 等。下面的机制和数字都来自该讲讲义及其引用图注,不是独立复测。

这一讲在解决什么浪费

开场提纲列出两条避免浪费的路径。一条是前缀缓存:相同的提示前缀不必再整段重做 prefill,同时要判断「去那台已经有缓存的机器」是否值得排队等待。另一条是把 prefill 和 decode 放到不同资源上。两条路都取决于 KV 状态住在哪、怎样迁移。

动机来自「写提示变得越来越贵」。课上引用类似 Karpathy 的观点:只靠提示往往比微调便宜一个数量级,于是系统提示、工具说明、长文档、多轮 agent 轨迹,都会把共享前缀拉得很长。讲义用 SGLang 等工作负载示意:工具问答、虚拟环境、代码生成、视频问答、长文档问答等,会形成一棵提示树——大量请求共享树干,再分叉到各自的叶子。

前缀为什么值得缓存,以及缓存有多贵

课上给出一个量级(引用 arXiv:2309.06180 一系材料):Llama 2 7B、FP16 下,一条请求大约 4096 个 token 的 KV 可到约 2GB。缓存不再是小小的备忘,更是显存级资产。

和传统缓存的对比也被写进讲义:传统缓存里,计算和状态常常可以分开,任意数据块都可能有用,成本也相对好估计;LLM 服务里的 KV 或前缀缓存,往往是算力和状态放在一起,主要是前缀才有复用价值,执行前要花多少计算、占多少显存也更难预知。

真实负载的特征,课上引用系统视角的研究和 Azure LLM Inference Trace:提示长、输出短,共享度高,波动大。于是盲目轮转等于每台 GPU 都重算整棵树;完全去追最长前缀,又可能把流量砸到少数热门 GPU 上,造成负载不均。

Preble:把复用和负载写成分布式调度

Preble(ICLR 2025,开源)被当作本讲的主系统:面向又长又共享的提示,把全局放置和本地公平调度放在一起设计。

全局调度叫 E2,也就是 Explore 加 Exploit。Exploit 是贪心复用:共享前缀足够长时,去已经有这段前缀的实例。直觉是前缀长度近似计算量;若共享前缀比其余独有部分更长,复用往往划算。对比一台全新机器,要同时付共享部分和独有部分的代价;回到旧机器,主要付独有部分,外加可能的驱逐代价。Explore 则是前缀感知的探路:为每张 GPU 估算负载代价,选最小的,避免全集群挤在同一棵热门前缀上。

架构草图是:分词之后,进入全局调度器和前缀树(全局与本地都有),再配合指标收集和再平衡;各 GPU 上做树匹配,并维护运行队列和公平等待队列。

本地则是感知前缀的公平调度。课上批评两类朴素策略:先到先服务用不了等待队列里的共享前缀;最长前缀匹配又可能饿死短前缀请求。Preble 的做法是轮转优先级组,组内再按先到先服务,在复用和公平之间找折中。

讲义给出的初始结果是讲义自报:Mistral-7B 跑在 2 张或 4 张 A6000 上,Llama 3 70B 跑在 8 张 H100 上;五类负载;对比 SGLang 和一种理想化的分区;指标包括平均时延、p99 时延和每秒请求数。相对基线,讲义称平均时延大约快 1.5 到 14.5 倍,p99 大约快 2 到 10 倍。课上还提到,2024 年前后生产侧对「更便宜的前缀共享」更感兴趣,并把 Moonshot 等写成受 Preble 启发一类叙述。

这一节的结论是:复杂提示让服务更贵,负载变得更长、更共享;要把缓存命中和负载均衡一起调度,而不是只在单机上做本地淘汰。

混批里,Prefill 和 Decode 怎样互相拖累

后半讲进入混批问题。一次很长的 prefill,会推迟已经在路上的 decode 的下一步。两条出路分别是:把长 prefill 切成小块,和 decode 交错执行;或者把两个阶段分到不同的 worker 上。权衡很清楚:共享算力、调度更细,还是隔离之后再付 KV 传输的开销。两者也可以组合。

课上用 DistServe 和 Hao AI Lab 的材料做了简短铺垫,并强调度量口径:每秒请求数只是成本的代理;更好的口径是 goodput,也就是满足服务目标的每秒请求数,更接近「每个合格请求花多少钱」。设计目标写成:用尽量少的 GPU 服务更多请求,同时满足各类应用的首个 token 时延(TTFT)和每个输出 token 的间隔(TPOT)。讲义里的举例是:聊天的 TTFT 可以放到大约 1 秒,搜索或编程大约 200 毫秒;编程的 TPOT 可以紧到大约 50 毫秒。这些都是讲义举例,不是普遍标准。

为什么两个阶段不能一视同仁,课上引用了 Sarathi 一系观察:prefill 主要受算力限制,一个请求就容易打满算力;decode 主要受访存限制,要很大的批次才吃得满算力。混在一起(1 个 prefill 加多个 decode)时,讲义用 LLaMA-13B、1 张 A100、提示长度 1024 的示意:新到的 prefill 可以把一步 decode 拉长约 12.6 倍(相对纯 decode)。这就是混批时的互相拖累。

资源与并行度还会被绑死。为了压低 TTFT 和 TPOT,常常靠加 GPU、提高张量并行或流水线并行。讲义示意:1 张 GPU 很难同时压过例如 TTFT 200 毫秒、TPOT 50 毫秒一类虚线;加到 4 张后两边都缩短,但钱也几乎线性上去,而且同一套并行策略要同时服务两类目标,很难按阶段单独选型。

DistServe:拆开阶段,按达标吞吐来配比

DistServe(OSDI 2024)的方案就是把 prefill 和 decode 拆开:请求先到 Prefill Worker,再把 KV 交给 Decode Worker。

讲义里有一笔示意账(2 个 prefill worker 配 1 个 decode worker;P90 TTFT 不超过 400 毫秒,P90 TPOT 不超过 40 毫秒)。共置时,prefill 侧大约到 3 请求/秒才能压住 TTFT,decode 侧大约 1.6 请求/秒才能压住 TPOT,系统达标吞吐取较小值,大约是每 GPU 1.6 请求/秒。拆成 2 加 1 之后,只做 prefill 大约 5.6 请求/秒,只做 decode 大约 10 请求/秒,于是

也就是大约每 GPU 3.3 请求/秒。这是讲义用来说明「拆开后可以各自打满瓶颈」的例子,前提是 KV 传输和池子配比跟得上。

讲义接着用四个问题组织设计:并行策略怎么配,用仿真加穷举搜索;KV 怎么高效传输,看 GPU 和 worker 怎么放置;突发怎么应对,用由 decode 主动拉取的传输;流水线空泡怎么减小,靠切块。拉取式传输的细节是:初始化时在 prefill 侧打开进程间通道,把句柄拷到 decode 侧;真正传输时由 decode 去拉 prefill 显存里的 KV;实现上提到 CUDA kernel 和 NCCL,并与张量并行组对齐。

评测侧展示了 ShareGPT、HumanEval、LongBench 上「输入远长于输出」的分布差异。例如讲义图中,ShareGPT 平均输入约 755.5、输出约 200.3;LongBench 输入约 1738.3、输出约 90.7。一种共置配比很难伺候所有应用。

讲义也写明了拆开何时会亏:最低 GPU 数会上升,单卡模型至少要一卡做 prefill、一卡做 decode;短请求省下的时间盖不住 KV 传输;突发不均时一侧空闲、一侧排队。因此应当先和同一资源池里的切块 prefill 对照,而不是默认拆开一定更好。

产业延伸页提到 NVIDIA Dynamo(感知 KV 的路由、P/D worker、NIXL 传输,页脚致谢 DistServe),SGLang 在大规模专家并行上的 PD 部署案例,以及 vLLM 的解耦 prefill:代理先把 prefill 的 max_tokens 设为 1,再把原来的 max_tokens 交给 decode,并用 drop_select 拉取 KV、跳过重算。

切块 Prefill:不拆池时,同一步里小步往前走

与解耦并列的是 Sarathi-Serve 路线:把长提示按块算完,每块复用已有 KV,并和 decode 混在同一次迭代里,限制单步占用设备的时间。

讲义强调:切块改变的是执行粒度,并不缩小最终 KV 的体积。块太大,单步并行度高,但这一步会拖很久;块太小,decode 更能插队,但启动次数多,前缀也要反复读。第一个 token 仍要等最后一块 prefill 算完;交错进来的 decode 还有可能拉长 TTFT。课上提醒:Sarathi 对外强调的收益,常常假设旁边已经有并发的 decode 可以一起排进同一步。

两条线怎么合,以及下一讲做什么

讲义把复用和解耦合在一起,写成四步:prefill 路由偏向可复用的前缀;decode 选显存和时延还有余量的目的地;异步传 KV 时跟踪所有权和生命周期;驱逐不得和传输或在途的 decode 抢同一块状态。

项目上的建议是:用固定轨迹,控制前缀共享比例,对比轮转、只追局部性、代价感知路由,再扫带宽、队列负载和上下文长度,报告 TTFT、TPOT、服务目标达成率、传输字节和缓存命中。

本讲带走的三点是:前缀复用是分布式放置问题;把 prefill 和 decode 分开,才能按阶段单独选资源;传输开销和负载倾斜决定收益大不大。下一讲才会进入长上下文状态和稀疏专家(MoE)服务,那才是张量并行、流水线并行、专家并行、序列并行和长 KV 迁移的主场;这一讲几乎不展开那些。

一点读后感

这一讲把大模型服务从单机持续组批,推进到两张调度图:一张管相同前缀放哪台机器,一张管算力饥渴的 prefill 和访存饥渴的 decode 是否还该抢同一张卡。Preble 和 DistServe 不是互相替代,而在提醒:只追命中率会不公平,只追拆分会付传输开销。读数字时要记住,它们来自特定模型、GPU 和服务目标;工程上更该带走的,是达标吞吐这个口径,以及先对照切块 prefill、再决定是否拆池的比较框架。

来源:Lecture 13 课件 · 课程主页 · 阅读材料 · 课程项目

Source details

meng shao · x.comPublished · added here

Original title: UCSD CSE 291A 第 13 讲:Prefix Caching 与 Prefill–Decode 解耦,分布式 LLM 推理怎样少做重复计算