07 / Inference & Serving进阶

推理服务也是训练系统的一部分

从 KV cache 与调度预算,理解 rollout 为什么等不到下一批数据

现代 RL 把生成放进训练内循环。一次服务优化会影响吞吐、行为策略分布、数据新鲜度和学习效率,不能只用 tokens/s 判断。

READ WITH A QUESTION

读完这一章,你应该能够

  • 估算 KV cache 的内存随上下文和并发增长的方式
  • 沿 Scheduler 的预算与队列解释连续批处理
  • 设计包含数值一致性和有效轨迹吞吐的服务评测
前置理解 训练与推理的区别 · 注意力与张量 shape
本章目录 · 0 个小节

从在线服务,回到训练内循环

传统预训练读取现成 token;在线 RL 要先让当前策略生成答案或完成 Agent 轨迹。推理不仅服务用户,也在生产下一次梯度更新所需的数据。课程分析:当 rollout 的生产速度比 trainer 消费速度慢,再优化一次反向 GEMM,可能只会让 trainer 更早进入等待。

训练需要的不只是文本,还包括 token IDs、行为策略版本、生成边界、必要的 log-prob、停止原因与工具观测。把这些内容仅序列化成对话字符串,再重新 tokenize,可能破坏动作位置与 loss mask 的对应。verl AgentLoop 数据接口

CODE / 可复制
任务队列:prompt / tool schema / generation config
推理队列:prefill → decode → 工具调用暂停
环境队列:CPU执行 → 新observation
恢复推理:追加token或重建上下文
轨迹完成:保存版本、log-prob、mask、stop reason
trainer:接收、过滤、重算必要概率、更新策略

为什么 prefill 和 decode 的瓶颈不同

Prefill 对输入序列进行大块计算;decode 常常每次只增加一个 token,但要读取已有参数和注意力状态。实际瓶颈取决于 batch、sequence length、架构、kernel 和设备,不能把“prefill 永远 compute-bound”当定律。

课程分析:使用 roofline 时先估算 arithmetic intensity:有效 FLOPs 除以从主存移动的 bytes。小 batch decode 可能低强度、带宽受限;更大 batch 增加权重复用,但 KV 内存也增加。长 prefill 则会挤占调度 token 预算、抬高短请求的 TTFT。应按 prefill 和 decode 分段看 profiler。

指标回答的问题单独看会误判什么
TTFT从提交到第一个token等多久包含排队,不全是prefill计算
ITL / TPOT生成过程是否流畅不能代表整条Agent完成时间
Output tokens/sdecoder吞吐可能在生产过长、无效的轨迹
Accepted trajectories/s训练实际接收速度还需看质量、难度、policy lag
Task success / wall time同等时间内是否更有效必须控制任务分布和预算

先算 KV,再谈并发

对标准全注意力 decoder,忽略 padding、元数据与分片,KV 内存可近似写为:

MKV=2×L×B×S×Hkv×dh×b.M_{KV}=2\times L\times B\times S\times H_{kv}\times d_h\times b.

2 代表 K 与 V,L 是层数,B 是并发序列数,S 是已缓存长度,HkvH_{kv} 是 KV head 数,dhd_h 是 head dimension,b 是每个元素的 bytes。GQA 用 KV head 而不是 query head;MLA、共享KV、滑窗、混合线性注意力需要重写公式,不能机械套用。

教学例子:L=32、B=8、S=8192、Hkv=8H_{kv}=8、dh=128d_h=128、BF16 两字节,得到 8 GiB KV。这里 GiB=2³⁰ bytes;设备标称带宽通常用十进制 GB/s。若只看模型权重大小来决定能开多少并发,很容易误判显存余量。

PagedAttention 把逻辑序列 KV 分成块来管理,减少连续预留带来的碎片和复制。它不会消除 KV 的所有内存,也不意味着改变注意力数学定义。论文及实验边界

精读调度器,只追一条调用链

本课把 vLLM 调度器固定到来源登记中的 commit,不跟随移动的 main。重点读 vllm/v1/core/sched/scheduler.py 中 Scheduler.schedule,再沿 KVCacheManager 的块分配接口确认预算与状态迁移。固定源码、符号与摘录

按三轮阅读:

  1. 找等待队列、运行队列与 preempted 状态,理解请求什么时候被接纳。
  2. 跟踪 token_budget、每请求需要的 token 数和 KV 分配失败路径,理解谁占掉了剩余预算。
  3. 追到 scheduler output 与 worker execution 的交接,检查完成结果如何更新请求状态。

源码观察:本次固定实现用统一的 token 数量和已计算数量安排执行;你应把“连续批处理”理解为反复重新组织活跃请求的工作,而不是等一整个静态 batch 全部结束。源码所支持的配置项不等于所有部署都启用它们。

输入是请求状态与资源预算,输出是本轮调度决定;调度函数自身不执行所有 GPU kernel。读到 Python 队列,就要继续问实际计算在哪个 worker、哪个 stream,以及延迟是否来自 host scheduling。

四条优化路线,各自解决什么

路线目标代价与需要验证的条件
Continuous batching减少完成长度不齐造成的空位调度开销、KV压力和排队公平性
Chunked prefill让大prompt与decode共享迭代预算长prompt TTFT可能上升;chunk越小不一定越好
Prefix caching复用相同前缀的已算KV必须精确匹配token/模板/相关配置,不能复用不同语义状态
Prefill/decode分离分别配置适合的资源并减少干扰KV迁移网络流量、队列协调与额外故障面

这些路线的选择属于系统设计;不要通过看到一个旗标就断言某头部模型使用了相同生产部署。

FlashAttention 通过改变注意力计算的 IO 安排避免显式落地大注意力矩阵,与 serving 的分页管理是不同层次。两者可以协作,但“使用 FlashAttention”既不自动产生连续批处理,也不自动解决 KV 分配。官方实现与论文入口

推理快了,RL 为什么反而不稳定

课程分析:低精度权重、不同 sampling 实现、tokenizer/chat template 变化、停止规则与训练重算的 log-prob 差异,会影响实际行为策略。若 log-prob 来自另一版本或另一序列边界,importance ratio 会错误,即使吞吐显著变好也不代表更有效学习。

建议一次性能变更至少报告四组对照:

  • 固定 prompt 与 seed 的分布诊断:log-prob 差异分位数、停止原因、输出长度。不要要求不同 kernel 必须逐 token 完全一致,先定义容差和统计比较。
  • 资源诊断:KV 使用、排队时间、prefill/decode 时长、每卡负载,记录 p50/p95 而不只均值。
  • 训练接口:mask、version、behavior log-prob 是否完整;拒收比例是否变化。
  • 任务诊断:相同 budget 和 harness 下的成功率与 cost per success。

长 Agent 任务可以在工具执行时释放 GPU 的执行位置,但仍需管理上下文状态与前缀生命周期。没有活跃 kernel,不代表这条任务不占 KV 或 CPU 环境资源。

运行调度实验,识别它没有模拟什么

CODE / 可复制
python3 -m labs.run --experiment rollout
python3 -m labs.run --experiment rollout --output rollout.json --trajectories trajectories.jsonl

本地实验把任务时长、worker 与 trainer 消费显式建模,并在 ingest 时检查版本差。浏览器时间线 用更小的固定任务集帮助观察屏障;二者模型假设与默认参数不同,数字不应直接对齐。它们都是离散事件仿真,不是 GPU benchmark。

先固定任务集比较同步与异步,再收紧 freshness 阈值,观察“完成吞吐”和“接收吞吐”分离。如果仅提高 worker 数,却导致更多陈旧轨迹被丢掉,就需要重新调节训练/推理资源比例和参数同步频率。

一份能落地的排障顺序

现象可疑位置下一项观测
GPU闲但队列长CPU调度、tokenization、环境阻塞host profile、队列年龄、tool p95
输出t/s高但训练等待轨迹长尾、grader、数据打包episode完成率、各阶段队列深度
KV不断preempt并发或context预算超出容量活跃token数、recompute次数、free blocks
权重同步后质量跳变版本不一致、partial轨迹混版rank checksum、trajectory分段version
单请求快、多请求抖admission与batching策略latency随并发曲线、token budget使用率
agent benchmark突然变好harness、tools或budget改变冻结模型做harness消融

工程任务:设计一个输入长、输出短的 Agent 工作负载和一个输入短、输出长的推理负载。不要预先选相同优化配置。画出 KV 生命周期与三个队列,写出能证伪“瓶颈是 GPU 算力”的实验。再去多卡训练理解这些推理 worker 如何与 trainer 争用网络和设备。

MAKE IT RUN

把这一章放进实验里

调整变量,观察机制,再运行本地 Python 实验验证。

进入实验

本章证据与源码

引用代表相应来源支持的结论,不意味着公开了完整训练配方。

源码观察vLLM V1:token 预算调度与 KV cache 管理vllm-project · 6dc34b6334fe论文实验Efficient Memory Management for Large Language Model Serving with PagedAttentionKwon et al. · arXiv:2309.0论文实验FlashAttention: Fast and Memory-Efficient Exact Attention with IO-AwarenessDao et al. · arXiv:2205.1源码观察verl:多轮 Agent rollout 的标准化接口volcengine · 8e03c039f9a7

留下你的工程判断

记录一个还没有解释清楚的现象,或下一次实验要控制的变量。笔记仅保存在当前浏览器。

自动保存在本机 · 可在“我的学习笔记”导出
理解机制,比记住结论更重要。