
当训练和推理已经闭环,问题就从“系统能跑通”变成“瓶颈在哪里、规模如何扩展、代价如何被观察”。
第四组把视角推进到 worker 内部:actor/critic update 由训练 engine、micro-batch、sequence parallel 和 checkpoint engine 共同完成,并且新权重必须同步回 rollout 侧。到这里,我们已经知道一次 RL step 为什么不是单个训练循环。第五组要继续回答更贴近生产的问题:当这套闭环开始变大、变长、变异步以后,系统到底慢在哪里?
这一组的核心判断是:RLHF 性能不是一个 GPU 利用率指标,而是一张跨阶段的时间账和数据账。gen、reward、old_log_prob、update_actor、update_weights的计时会先暴露一轮 step 的主瓶颈;随后,DataProto 序列化、controller 数据回流、TransferQueue、Fully Async Policy、staleness、off-policy correction、MoE/EP/offload/reshard 又会把问题推向生产级 train/serve 系统。
先看第五组在系列里的位置。读这张图时注意三层:上层是一轮 RL step 的时间分解;中层是数据从同步 controller 流转到队列化、异步化系统的变化;下层是第五组六篇文章各自要解释的性能边界。

性能、规模与生产化总览
这张图的重点不是给出某个固定瓶颈结论,而是提供后续六篇的读法:第 25、26 篇先用 timing 把 step 拆开;第 27、28 篇把视角从 GPU compute 转向 DataProto、Future 和 TransferQueue;第 29 篇讨论异步策略如何用样本新鲜度换吞吐;第 30 篇回到超大规模模型,解释 MoE、EP、offload 和 resharding 为什么会把性能问题放大成系统设计问题。
第四组最后落在 checkpoint engine:训练权重更新完成后,rollout replicas 要拿到新权重才能继续产生下一轮样本。这个闭环建立以后,系统问题不再是“actor update 能不能完成”,而是“gen、reward、logprob、update、sync 哪个阶段控制了端到端吞吐”。
verl 的 RayPPOTrainer.fit()已经把这个问题写成代码结构。主循环用 marked_timer("step")包住一轮训练,又在内部拆出 gen、reward、old_log_prob、values、adv、update_critic、update_actor、save_checkpoint、update_weights和 testing。这些计时最终进入 compute_timing_metrics()与 compute_throughout_metrics()。也就是说,第五组不是先假设瓶颈在哪里,而是先把一轮 RL step 变成可观测对象。
这里有一个容易误解的点:如果只盯着训练后端的 forward/backward,很容易把吞吐问题理解成纯 GPU compute;但在 RLHF 里,rollout generation 的长尾、reward/logprob 重算、DataProto 序列化、controller 收发、权重同步暂停、异步队列等待,都会进入同一张时间账。第五组要补上的,就是这张账的读法。
第 25 篇《如何 profiling 一次 RL step》先回答观测问题:一次 step 应该怎么拆成 gen、reward、old_log_prob、update_actor、update_weights等阶段,marked_timer()、硬件 profiler 和 metrics 分别能证明什么,哪些结论只能算工程解释。
第 26 篇《RLHF 吞吐瓶颈在哪里》会把计时结果转成瓶颈地图。它关注 rollout generation 与 actor update 的关系:到底是推理侧长尾让训练等样本,还是训练侧 update 和权重同步让推理侧等待,或者两者在不同配置下交替成为主瓶颈。
第 27 篇《Data movement 是隐藏的大头》把注意力从 GPU kernel 拉回数据路径。DataProto.__getstate__()、__setstate__()、TensorDict 序列化、non-tensor batch 和 DataProtoFuture说明,controller 不是免费的胶水;数据如何被 pickled、ray.get、concat、dispatch,会直接影响大规模 step 的尾延迟。
第 28 篇《TransferQueue:从单 controller 数据流走向流式数据系统》解释队列化路线。TransferQueue 把 agent loop 产物写入外部队列,ReplayBuffer 周期性 poll 元数据,再按训练需要取样。这不是简单替换一个容器,而是在把“controller 等所有 rollout 返回”改造成“生产/消费解耦”的数据系统。
第 29 篇《Fully Async Policy:速度、新鲜度和 off-policy 的三角关系》进入异步策略。Fully async rollouter 会持续产样本、写 message queue,trainer 从队列里凑 batch,并在参数同步后 reset staleness。吞吐提升的代价是样本可能来自旧权重,因此必须同时读 staleness threshold、partial rollout、rollout correction 和 off-policy metrics。
第 30 篇《671B/MoE 级别训练暴露了哪些 infra 问题》把问题推到极限规模。到了 MoE 和数百 B 参数级别,瓶颈不再只是 step 中哪个阶段慢,而是 expert parallel、context parallel、CPU offload、optimizer offload、distributed checkpoint、weight resharding 和推理侧权重更新共同决定系统是否还能稳定运转。
第一个边界是阶段计时和端到端吞吐。timing_raw["gen"]很长,不一定意味着 rollout backend 单独有问题;它可能包含 request 长尾、agent loop 行为、sleep/resume 时机和后续同步压力。第五组读 timing 时会尽量把“观测到的时间”与“推断出的原因”分开。
第二个边界是 compute 和 data movement。Tensor 在 GPU 上算得快,不代表跨 controller、worker、Ray object store 和队列系统搬得也快。DataProto、DataProtoFuture 与 TransferQueue 的差异,正是从“函数返回一个 batch”走向“分布式数据系统”的边界。
第三个边界是异步吞吐和策略新鲜度。Fully Async Policy 可以减少 trainer/rollouter 的互相等待,但它不会免费保持 on-policy。staleness、importance sampling、rejection sampling 和 partial rollout 都是在为这个速度收益付账。读第 29 篇时,重点不是异步一定更快,而是看它怎样管理旧样本风险。
到第四组结束,系列已经补齐了 training engine/distributed core和 weight sync。第五组继续向生产化推进:先把一轮 RL step 变成可观测的时间账,再解释吞吐瓶颈、数据搬运、队列解耦、异步策略和超大规模模型如何改变系统边界。
放回系列地图,第五组补的是最后一段:从能闭环的 train/serve 系统,走向可 profile、可扩展、可权衡的生产系统。
training objective
-> dataflow
-> controller
-> workers/resources
-> rollout/serving engine
-> weight sync
-> performance/scale/production system
读完这一组,读者应该能拿着一次 RL step 的 timing,判断瓶颈属于 rollout、update、data movement、weight sync 还是异步队列;也能理解为什么 671B/MoE 级别的问题,最终会落到并行维度、offload、reshard、权重新鲜度和系统流控的组合约束上。
verl/trainer/ppo/ray_trainer.py:1373-1642:一轮 RL step 如何被拆成 gen、reward、old_log_prob、update_actor、update_weights等计时段,并汇总 timing/throughput metrics。verl/utils/profiler/performance.py:140-260:marked_timer()、reduce_timing()和 gather_timing()的基础计时与分布式聚合逻辑。verl/protocol.py:247-293、377-424、1174-1228:TensorDict 序列化、DataProto pickling 和 DataProtoFuture 的异步数据传递边界。verl/trainer/main_ppo_sync.py:17-22、193-219、367-437、463-490:TransferQueue 路线中 ReplayBuffer poll、agent loop 输出入队和非阻塞生成入口。verl/utils/transferqueue_utils.py:34-62、111-153、213-230:TransferQueue 缺省 mock、metadata 转真实数据和函数输出写回队列的桥接逻辑。verl/trainer/ppo/rollout_corr_helper.py:14-60:rollout correction 对 off-policy、staleness 和分布漂移的定位。verl/experimental/fully_async_policy/fully_async_trainer.py:273-330、501-518:trainer 如何从 message queue 收集样本,以及参数同步后 reset staleness。verl/experimental/fully_async_policy/fully_async_rollouter.py:392-418、562-590:fully async rollouter 的职责、staleness threshold 和 reset 指标。verl/workers/config/engine.py:150-205、390-555:Megatron/MCore、TorchTitan、Automodel 路径中的 EP、CP、offload、reshard 和 MoE 配置入口。verl/workers/engine/megatron/transformer_impl.py:94-123、148-214、355-397、502-597:Megatron engine 的 offload 标志、并行维度注入、checkpoint/offload 生命周期。