推理服务也是训练系统的一部分
从 KV cache 与调度预算,理解 rollout 为什么等不到下一批数据
现代 RL 把生成放进训练内循环。一次服务优化会影响吞吐、行为策略分布、数据新鲜度和学习效率,不能只用 tokens/s 判断。
读完这一章,你应该能够
- 估算 KV cache 的内存随上下文和并发增长的方式
- 沿 Scheduler 的预算与队列解释连续批处理
- 设计包含数值一致性和有效轨迹吞吐的服务评测
本章目录 · 0 个小节
从在线服务,回到训练内循环
传统预训练读取现成 token;在线 RL 要先让当前策略生成答案或完成 Agent 轨迹。推理不仅服务用户,也在生产下一次梯度更新所需的数据。课程分析:当 rollout 的生产速度比 trainer 消费速度慢,再优化一次反向 GEMM,可能只会让 trainer 更早进入等待。
训练需要的不只是文本,还包括 token IDs、行为策略版本、生成边界、必要的 log-prob、停止原因与工具观测。把这些内容仅序列化成对话字符串,再重新 tokenize,可能破坏动作位置与 loss mask 的对应。verl AgentLoop 数据接口
任务队列: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/s | decoder吞吐 | 可能在生产过长、无效的轨迹 |
| Accepted trajectories/s | 训练实际接收速度 | 还需看质量、难度、policy lag |
| Task success / wall time | 同等时间内是否更有效 | 必须控制任务分布和预算 |
先算 KV,再谈并发
对标准全注意力 decoder,忽略 padding、元数据与分片,KV 内存可近似写为:
2 代表 K 与 V,L 是层数,B 是并发序列数,S 是已缓存长度, 是 KV head 数, 是 head dimension,b 是每个元素的 bytes。GQA 用 KV head 而不是 query head;MLA、共享KV、滑窗、混合线性注意力需要重写公式,不能机械套用。
教学例子:L=32、B=8、S=8192、、、BF16 两字节,得到 8 GiB KV。这里 GiB=2³⁰ bytes;设备标称带宽通常用十进制 GB/s。若只看模型权重大小来决定能开多少并发,很容易误判显存余量。
PagedAttention 把逻辑序列 KV 分成块来管理,减少连续预留带来的碎片和复制。它不会消除 KV 的所有内存,也不意味着改变注意力数学定义。论文及实验边界
精读调度器,只追一条调用链
本课把 vLLM 调度器固定到来源登记中的 commit,不跟随移动的 main。重点读 vllm/v1/core/sched/scheduler.py 中 Scheduler.schedule,再沿 KVCacheManager 的块分配接口确认预算与状态迁移。固定源码、符号与摘录
按三轮阅读:
- 找等待队列、运行队列与 preempted 状态,理解请求什么时候被接纳。
- 跟踪
token_budget、每请求需要的 token 数和 KV 分配失败路径,理解谁占掉了剩余预算。 - 追到 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 环境资源。
运行调度实验,识别它没有模拟什么
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 争用网络和设备。
把这一章放进实验里
调整变量,观察机制,再运行本地 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留下你的工程判断
记录一个还没有解释清楚的现象,或下一次实验要控制的变量。笔记仅保存在当前浏览器。
自动保存在本机 · 可在“我的学习笔记”导出