先算能接几条,再谈调度。
教学用圆整,按 7B 量级:32 层,hidden 4096,fp16 每数 2 字节。公式:
KV ≈ 2 × layers × hidden × seq × batch × bytes 一条、一个 token: 2 × 32 × 4096 × 2 = 524,288 字节 ≈ 0.5 MiB 长度 2048、并发 8: 0.5 MiB × 2048 × 8 ≈ 8 GiB
权重还要另放。7B 的 fp16 权重大约 14 GiB。24 GiB 的卡,KV 再涨,新请求就会被拒、被抢、被换出。被挤下去的那一次,写进别人的 P95。这组数字是教学圆整,现场用你自己的层数、KV head、精度重算,不要把 8 GiB 抄进容量报告。
连续批处理把新请求塞进正在解码的批次,吞吐上去。平均 TTFT 可能几乎不动。一条 8k 的长请求插进来,prefill 要先吃完。门口那条 32 token 的短问题就得等这一段。P95 TTFT 从 0.9 秒跳到好几秒,P95 ITL 还可能看起来正常——字一旦开始出,间隔还行。用户骂的是第一下。客服转述「卡住了」,多半是 TTFT,不是 ITL。两者要分开画,混成一个「模型延迟」,你会去调温度。
原版里有过一次教材事故:把并发从 64 调到 256,总吞吐升 18%,平均 TTFT 几乎没变,P95 TTFT 从 0.9 秒到 6.4 秒,KV 顶在 97%,五分钟抢占三百多次。卡更忙了,客户更慢了。忙和慢可以同时成立。那次如果告警绑的是平均值,值班不会响。绑 P95 和 KV 余量,会在调参当晚就叫人起来。
长度一翻倍,KV 翻倍。并发再翻倍,再翻倍。上面那 8 GiB 是 2048×8。改成 8192×8,KV 大约 32 GiB,已经超过一张 24 GiB 的卡。请求不是「慢一点」,是根本接不住,调度器开始抢占。抢占一次,那条短请求的 TTFT 就从百毫秒跳到秒级。日志里 GPU util 仍然好看。
值班时先看 KV 还剩多少、队列里有没有长请求、短请求的 TTFT 分桶。不要只看 GPU 利用率和平均延迟。长请求可以隔离、可以限长、可以降到慢队列。不隔离,工作点再便宜也稳不住。连续批处理是加速器,不是免费午餐。谁进批次,谁就在决定别人的第一下。隔离可以很土:长于 2k 的请求进慢队列,短请求保 2 秒。土办法能让 P95 活下来。平均好看、尾巴死人,就是没隔离。
想看调度怎么插队,可选玩具(合成,不能当现场成绩):inference-scheduler-lab