大模型收到一段输入后,要先把整段 Token 并行算一遍,建立每层的 KV 缓存。第一个输出出现以后,模型转入逐词生成,每一步只增加一个 Token。前一段叫预填充,后一段叫解码。两段工作原来常在同一批 GPU 上交替运行,预填充解耦则把它们分给不同设备。
我查了 DistServe 论文及其公开实现。论文把问题落在两个用户能感到的时间上。预填充主要影响首词元延迟,解码主要影响每个输出 Token 之间的间隔。两段计算挤在同一张卡时,一条很长的输入会拖住正在逐词回答的请求;解码任务太多,也会让新请求迟迟拿不到第一个输出。
两段计算需要不同的安排
两段很不一样。预填充一次处理许多 Token,计算量会随输入长度明显增长。解码每次只处理一个新 Token,却要反复读取模型权重和已有 KV 缓存,常受显存带宽限制。系统若强行给两段使用同一套并行度和同一批资源,只能在首词元延迟与逐词间隔之间来回让步。
DistServe 让预填充 GPU 算出首个输出和 KV 缓存,再把这些中间结果交给解码 GPU。预填充侧可以按输入长度和首词元目标选择并行方式,解码侧可以积累更大的批次,专门守住逐词延迟。论文还用 goodput 衡量在延迟目标内完成的请求量,超时的请求不会因为最终做完了就算作有效产出。
这项拆分会增加一次很实在的数据搬运。论文给出的例子中,OPT 66B 处理一条 512 Token 输入后,KV 缓存约为 1.13GB。若每秒到达十条请求,传输需求约为每秒 11.3GB,也就是 90Gbps。高速 InfiniBand 或同机 NVLink 能承担这笔流量,普通网络环境就可能把省下的排队时间重新花在传输上。
设备放在哪里因此很关键。预填充与解码跨低带宽节点,KV 缓存会走得太久;全塞进同一节点,又可能无法按各自负载增加设备。DistServe 为高速跨节点网络和依赖节点内互连的集群设计了不同放置办法,这也说明论文里的收益离不开硬件条件。
扩容时要分别看两条延迟
论文在聊天、代码助手和文档摘要负载上测试多种模型与延迟要求,报告最高可多服务 7.4 倍请求,或满足严格 12.6 倍的服务目标,同时让九成以上请求处在限制内。这是其测试集群和工作负载的结果,不能直接换算成任意线上系统的节省比例。
实际压测应同时记录首词元延迟、每词元延迟、输入长度、输出长度与 KV 传输时间,再看中位数和尾部。长文档很多、逐词体验要求紧、互连带宽充足时,预填充解耦更有机会发挥作用。请求很短或流量很低时,两套资源和调度逻辑可能只增加复杂度。
我会先画出预填充 GPU、解码 GPU 与网络三处的时间线。拆开以后,任何一处出现排队,问题都应回到设备数量、并行度与放置方式上查。仅看整机吞吐,很容易漏掉用户已经等得不耐烦的那一段。