W04 · S22—S28 · 总 Day 112—118
推理服务:吞吐、等待与长尾的交换
比较逐请求与固定窗口批处理,逐条追踪等待时间。
作者准备的学习示例 · 不计真实学习进度 · 不代表生产 / GPU / 真机结果
核心问题
单个请求只要几毫秒,用户为什么等很久?因为服务时间不等于响应时间。排队、凑批、共享资源、长请求阻塞和返回过程都可能进入用户等待。
对应 S22~S28。先用不含模型的离散事件例子拆清量,再回到真实 LLM 的 prefill/decode 与 KV cache。
1. 本例的时间模型
八个请求按固定时间到达,其中第一个 work=20,其余 work=2。每批有 2ms 固定开销,同批计算时间假设为最大 work,所有成员一起结束。这是假设可以理想并行的乐观模型,不是实测 GPU kernel。
每批开始时间至少为“上一批结束”和“最老待处理请求到达 + 固定窗口”两者的最大值。窗口为 0、batch size 为 1 时相当于串行;窗口为 3、batch size 为 4 时允许聚合。这里没有抢占,也不在 decode 每步重新调度,因而不是 continuous batching。
wait = start - arrival;latency = finish - arrival。吞吐按有限工作负载的总请求数除以首个到达至最后结束的时间,包含起停效应,不是稳态容量。
2. 跟着一条请求计算
npm run learning:p2 -- w04
串行中 r1 在 0 开始、22 结束;r2 同在 0 到达,只能在 22 开始、26 结束。因此 r2 工作量为 2,但响应延迟为 26。排队才是主要部分。
批处理中第一批在 3 开始、25 结束;r1 的延迟从 22 增到 25,部分后续请求却更早完成。整组有限请求的吞吐从 160 到约 275.86 请求/秒,nearest-rank p95 从 44 降到 26ms。八个样本的 p95 实际取最大值,不能代表稳定生产分位数。
这个结果说明“整体改善可能让某个请求变慢”,不说明“批越大越好”。默认输入有密集到达与理想批内并行,已经偏向批处理。换成稀疏请求,凑批时间可能成为纯粹的额外等待。
3. 改动与解释
只把各请求 arrival 改为 i * 100,保持 work 不变。大多数批只能收进一个请求;固定窗口无法摊薄开销,却增加单请求延迟。再恢复输入,把第一个 work 改成 200,观察同批短请求怎样被最长请求拖住。
如果想进一步理解调度,可以在纸上设计“短请求专用队列”,同时问:它是否让长请求饥饿?谁决定短长分类?分类错误怎样影响公平性?不需要增加复杂调度框架。
4. 接回真实 LLM 服务
真实系统还要区分排队时间、TTFT、相邻 token 延迟、总延迟、tokens/s 和满足时限的成功任务数。Prefill 消耗、decode 步数、KV 内存、取消请求、cache 命中和抢占会一起改变容量。当前输出没有 token,因此不得把 latencyMs 改名为 TTFT。
- Stanford CS336 2025:选 inference 相关课程,关注算力与内存带宽的不同瓶颈。
- vLLM Paged Attention:把真实 KV 布局与本例缺失的内存约束对照。本例不实现 PagedAttention。
- Google SRE:Implementing SLOs:思考该从用户体验定义怎样的 good event,而不是把模拟吞吐当服务承诺。
5. 向实时智能延伸
Agent 的在线规划可能有交互等待预算;机器人控制还可能有严格截止时间和过期动作风险。提高吞吐不一定改善闭环稳定性。此处只能迁移“端到端时延和排队必须一起分析”的原则,不能把毫秒模拟当作满足真机实时要求。
一句话笔记:在这组输入中,哪个请求付出了代价,哪个假设最有利于批处理?