RLHF & RL Training Infrastructure

RLHF & RL Training Infrastructure for LLMs

The systems stack that post-training reinforcement learning runs on — RLHF, RLVR, reasoning-RL, and agentic-RL all share it. This is deliberately not the RL algorithm (PPO/GRPO/DPO — those live in the alignment, reasoning, and agentic-RL references) and not generic supervised distributed training (FSDP/ZeRO for pretraining — that lives in distributed-training). It is the third thing those two references keep pointing at: how do you actually run an RL post-training job — generate samples, score them, update weights, and not leave half your GPUs idle.

The one-sentence framing: a supervised step is one engine doing forward+backward; an RL step is three engines — a generator, a scorer, and a trainer — passed through an experience buffer, and the whole discipline exists because the generator dominates wall-clock (60–90%+ of step time) and the naive synchronous schedule leaves the trainer’s GPUs idle while the generator works. Every design decision below — colocate vs disaggregated placement, the per-step weight resync, async/off-policy systems, the train/inference logprob mismatch — follows from attacking that bottleneck.

Scope boundary (read first)


1. The actor–rollout–learner architecture (the three engines)

An RL post-training step decomposes into three logically distinct engines that exchange data through an experience buffer:

Model count by algorithm. Classic PPO-RLHF runs four models: actor (policy, trained), critic/value (trained), reward model (frozen), and reference model (frozen, for the per-token KL penalty). Critic-free algorithms (GRPO and kin) drop the value model, collapsing the systems problem to actor + reward/verifier + reference — one fewer trained network to place, shard, and resync. (Why GRPO can drop the critic is an algorithm question → reasoning-models; that it removes an engine from the topology is the systems consequence that matters here.)

The per-step dataflow: prompts → (rollout) responses → (reward) scores + (recomputed) logprobs → advantages → (learner) gradients → updated weights → resync to the rollout engine (§4). That last arrow — pushing fresh weights back into the generator every step — is the loop-closing step that supervised training does not have, and it is the source of much of the difficulty.

The design space (HF survey). The “Keep the Tokens Flowing” survey frames the whole field as seven orthogonal axes; the three that matter most are: (1) the orchestration / concurrency primitive (Ray actors, asyncio, pub/sub, HTTP); (2) the rollout buffer design (how rollouts flow from inference into training); (3) the weight-synchronisation protocol. The remaining axes are sync/async degree, GPU placement, batching, and reward integration. A framework is essentially a point in that 7-axis space — knowing the axes lets you read any framework’s design in minutes.


2. Co-located / hybrid vs disaggregated GPU placement

The central placement decision: do the trainer and the rollout engine share one GPU pool (colocate / hybrid) or run on separate pools (disaggregated)? This single choice cascades into the weight-resync mechanism (§4), the utilization profile (§9), and how easy async is (§5).

The programming model underneath (HybridFlow’s core contribution). RL dataflow is awkward because it is nested and multi-model. HybridFlow combines a single-controller paradigm (one process expresses the whole dataflow graph — flexible, easy to express PPO/GRPO/DAPO) with a multi-controller paradigm (each device runs its own SPMD program — efficient, low dispatch overhead). Pure single-controller has high control-dispatch overhead at scale; pure multi-controller is too rigid for nested RL dataflow. The hybrid reports 1.53×–20.57× throughput over baselines like DeepSpeed-Chat and NeMo-Aligner. ROLL and slime use the same single-controller + parallel-worker abstraction. Practical read: colocate when GPUs are scarce and you want max utilization; disaggregate when you want isolation, independent scaling, or fully-async (§5).


3. The generation / rollout bottleneck (inference-engine-in-the-loop)

The dominant systems fact: RL post-training is rollout-dominated — generation accounts for 60–90%+ (up to >90% worst case) of total RL step time. The reason is structural: autoregressive per-token decoding is memory-bandwidth-bound and runs at <40% GPU utilization in the actor, whereas the scoring and training stages are compute-intensive. (The prefill-compute-bound vs decode-memory-bound mechanics are a kernel-layer fact → llm-gpu-kernels; the consequence — generation is the expensive stage — is what drives every choice here.)

This is why frameworks plug a dedicated inference engine — vLLM or SGLang — into the loop as the rollout generator (with PagedAttention, continuous batching, often FP8/INT8 inference for extra speed), rather than generating with the training framework’s slow eval path. veRL supports both vLLM and SGLang as interchangeable rollout backends; slime is SGLang-native; OpenRLHF/TRL default to vLLM.

The long-tail straggler problem compounds it. Response lengths are long-tailed, so a few very long generations stall an entire synchronous batch — most GPUs sit idle waiting on the slowest few sequences. This one fact — “rollout is the bottleneck and the tail makes it worse” — is the direct motivation for async (§5), overlap (§9), and partial-rollout recycling (APRIL). If you remember one thing about RL systems: optimize the rollout, or nothing else matters.


4. The train → infer weight resync (weight transfer)

After every policy update, the learner’s new weights must be pushed into the rollout engine before the next generation — otherwise the generator samples from a stale policy. This per-step weight resync is a notable systems cost and a frequent source of subtle bugs. It is the step supervised training simply does not have.

Transfer mechanisms:

The hard part — resharding across mismatched layouts. The trainer is sharded one way (FSDP, or Megatron TP×PP×…), the inference engine another (vLLM/SGLang TP). veRL handles this with sharding managersFSDPVllmShardingManager and MegatronVLLMShardingManager — that reshard actor→rollout weights on the fly; the in-place zero-copy reshard is only possible in colocated engine mode (disaggregated must send weights over the network). slime exposes the same split as two APIs: UpdateWeightFromTensor (colocated) vs UpdateWeightFromDistributed (multi-node). Dedicated tools now target this step specifically — Ant’s AWEX advertises “second-level parameter updates from training to inference.” vLLM has added native weight-syncing APIs plus sleep/wake support so a colocated engine can free KV-cache memory during training and reload weights on wake. Practical read: weight resync is where colocate (cheap IPC/in-place) and disaggregate (network broadcast) diverge most sharply, and it is a top source of “the reward curve is stuck” bugs (§10b).


5. Async / off-policy RL systems (staleness, streaming rollout)

Synchronous RL forces the trainer to wait for the slowest rollout (the §3 tail). Asynchronous RL decouples generation from training: rollout workers generate continuously while training workers update whenever a batch is ready. The price is off-policy staleness — rollouts were produced by an older policy than the one being updated, which biases the gradient and must be corrected (and is the systemic root of the logprob mismatch in §10a).

AReaL (Ant Research) — the canonical fully-async system. Four pieces: streaming generation (each rollout worker generates without waiting), interruptible rollout workers, dynamic batching for variable-length outputs, and a parallel reward service. It uses a staleness-controlled, modified PPO that tolerates samples up to 8 steps old with no performance drop, plus a data-filtering step to cap staleness — achieving ~2× speedup at equal final accuracy. AReaL-Hex extends this to heterogeneous GPUs (mixed device types in one async job).

The sync↔async spectrum (not a binary):

NeMo-RL and OpenRLHF both ship async rollouts + replay buffers for off-policy training; TRL ships an async GRPO trainer. Practical read: more async = more throughput and more staleness you must correct (TIS, §10a). Most teams start one-step-off or partial-rollout (APRIL) before reaching for fully-async.


6. The framework landscape (2024–2026)

Framework Org Training backend Rollout backend Orchestration Default placement Notes
veRL / HybridFlow ByteDance FSDP, Megatron vLLM, SGLang hybrid single+multi-controller colocate (3D-HybridEngine); supports disaggregated Most-used; in-place zero-redundancy reshard; AgentLoop for multi-turn; 1.53–20.57×
OpenRLHF community DeepSpeed ZeRO-3 vLLM Ray Placement Groups disaggregated / separated First production Ray+vLLM+DeepSpeed; PPO/GRPO/REINFORCE++/async-agentic; Adam-on-CPU option
NeMo-Aligner NVIDIA Megatron-LM 3D parallel TensorRT-LLM colocate 1000s of GPUs (Nemotron-4 340B, Llama-3.1 405B); TRT-LLM-accelerated generation
NeMo-RL NVIDIA Megatron, FSDP vLLM, SGLang Ray both Re-architected NeMo-Aligner; FP8 training, VLM SFT/GRPO, fully-async GRPO
TRL HuggingFace Accelerate / FSDP / DeepSpeed vLLM (colocate or server) process / HTTP colocate or server mode GRPOTrainer/PPOTrainer; NCCL weight sync every weight_sync_steps; async GRPO; most accessible
ROLL Alibaba Megatron vLLM / SGLang Ray, single-controller + parallel worker both Rollout scheduler w/ per-sample lifecycle; env+reward workers for agentic; 200B+ MoE
slime THUDM / Z.ai Megatron-LM SGLang-native Ray, HTTP colocate or decoupled, sync or async Deep SGLang integration; SlimeRouter, StringRadixTrie cache; UpdateWeightFromTensor/Distributed
AReaL / AReaL-boba Ant FSDP / Megatron SGLang fully async disaggregated Streaming/interruptible rollout, staleness ≤8 steps, parallel reward service, ~2×
SkyRL NovaSky / Anyscale FSDP vLLM Ray disaggregated Inference stack as tokenization source-of-truth; VLM RL; full-stack (train/agent/gym)
TorchForge / torchtune RL Meta / PyTorch PyTorch-native FSDP vLLM PyTorch-native post-training; thinly documented publicly beyond the HF survey

The survey’s meta-finding: 16 libraries built independently converged on the same fixget off the synchronous pipeline, because idle training GPUs are the throughput killer. They differ mainly along the seven axes (§1). Choosing: veRL if you want the most-used, hybrid, in-place-reshard default; OpenRLHF for Ray-disaggregated; NeMo-RL/-Aligner at the largest (Megatron + TRT-LLM) scale; TRL for accessibility and a gentle on-ramp; slime for SGLang-native; AReaL for fully-async SOTA throughput; ROLL for an agentic-friendly rollout scheduler.


7. Reward-model serving + verifier / code sandboxes in the loop

The reward source is itself a served system component, not a passive function — and at scale it can become the bottleneck.

Systems concerns specific to the reward stage: the verifier/reward can become the bottleneck (unit-test test-time-scaling shows a clear performance–latency trade-off); at scale teams serve hundreds of environments as autoscaled managed sandbox endpoints (e.g. serving 330+ RL environments backed by 4.5M+ tasks on autoscaled sandbox compute). Sandbox isolation and throughput — parallel execution, timeouts for non-terminating generated code, side-effect containment — are first-order infra problems. The standing warning: verifier quality is the real bottleneck — weak auto-generated reward functions teach the wrong behavior at scale (the systems face of reward over-optimization, §10c). (The agentic-RL flavor of environments — Gymnasium step/reset, OpenEnv, SkyRL-Gym — is detailed in agentic-rl §3; here the focus is serving the reward/verifier as throughput-critical infra alongside the rollout engine.)


8. Scaling the trainer (FSDP/Megatron) alongside the rollout engine (TP)

Training and inference want different parallelism layouts — and that mismatch is the root reason the weight resync (§4) is non-trivial.

When colocated, the two layouts contend for the same GPU memory, so frameworks offload the trainer (params/optimizer to CPU or freed) while generating, then reload — exactly what vLLM sleep/wake and the 3D-HybridEngine reshard enable. When disaggregated, the two pools size independently but pay the network resync. veRL exposes both FSDP and Megatron trainer backends behind a common worker API and maps them to vLLM/SGLang rollout workers.

Boundary: the supervised parallelism mechanics — ZeRO stages, FSDP2 internals, TP/PP/CP/EP composition, NCCL collective tuning — belong to distributed-training. This reference owns only the RL-specific train-vs-infer layout mismatch and the resharding it forces. If the question is “how do I shard a 405B model to train it,” that is distributed-training; if it is “why do my trainer and generator disagree on layout and how do I bridge them every step,” it is here.


9. RL-specific throughput & GPU under-utilization (the “bubble”)

In a naive synchronous PPO/GRPO loop the stages run sequentially with hard dependencies: the reward model cannot score until the actor finishes generating; the learner cannot step until rewards are in. This creates an idle “bubble” — generation GPUs busy while training GPUs idle, then vice-versa — amplified by the long-tailed response lengths (§3). Measured actor-generation utilization is <40%.

Mitigations (distinct from full async, §5 — these keep an on-policy-ish schedule):

Reported gains: OPPO 1.8×–2.8× end-to-end with 1.4×–2.1× higher GPU utilization, no convergence loss; APRIL +20–44% rollout throughput.

Orchestration corollary: classic Slurm gang-scheduling fits supervised training but not the heterogeneous, long-lived, multi-role RL job (a generator pool + a trainer pool + a reward service, each a different shape, all long-running). This is why these systems lean on Ray rather than Slurm — “RL doesn’t work on Slurm” is a recurring lesson, because RL is not one homogeneous gang of identical workers.


10. Failure modes unique to RL systems

10a. Train/inference logprob mismatch — the headline systems bug

The rollout engine (vLLM/SGLang) and the trainer (FSDP/Megatron) produce different log-probabilities for the same sequence under the same weights, because they use different kernels, precision, and batching paths. This silently turns nominally “on-policy” RL into off-policy RL with nontrivial bias — the behavior policy (inference) differs from the proxy policy (training) even before any async staleness is added.

10b. Weight-sync bugs

Stale or partially-synced weights leave the rollout engine generating from an old policy. Real-world example: vLLM weights silently not synchronized when vllm_enable_sleep_mode=True (the sleep/wake path skipped the update). Symptoms look like a “stuck” or diverging reward curve — easy to misdiagnose as an algorithm problem when it is a resync (§4) bug. When the reward curve is flat or wrong, verify the generator actually received the latest weights before touching hyperparameters.

10c. Reward over-optimization / hacking at scale (systems symptoms)

As RL scales, the policy exploits flaws in the reward source — reward keeps rising while true quality stalls or drops (Goodhart). Execution-based code rewards are more robust than learned RMs, which suffer instability and hacking. The algorithmic mitigations (KL penalty, RM ensembles, ODIN) live in the alignment-algorithm domain (llm-alignment-post-training); the systems responsibility here is verifier quality, sandbox correctness, and reward-service monitoring — i.e. making sure the thing the policy is gaming is actually correct and observable.


Practical patterns

Anti-patterns

Troubleshooting

References (primary sources)