Suboptimal forward dispatch order in make_experience causes unnecessary serialization
Maintainers usually reply within 1 day
@RainieLLM is already working on this.
Since Sep 18, 2026.
Assessment
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Newbie friendliness
- 28/100
- Issue type
- Refactor
- Clarity
- Clearly specified
- Activity status
- Stale
- Tech stack
- python
- Domain
- performance
Research direction
Read openrlhf/trainer/ppo_utils/experience_maker.py around RemoteExperienceMaker.make_experience() and the _dispatch_forward helper, tracing how args.train.colocate_all, colocate_actor_ref and colocate_critic_reward decide where ray.get blocks. Note that linked PR #1359 already targets this, so check it before starting. Done means the reward/actor batch is dispatched before the sync barriers and critic/reference dispatch overlaps, with _dispatch_forward's signature unchanged and all three flag combinations still correct.
Written by the indexing model from the issue text.
Description
Problem
In RemoteExperienceMaker.make_experience() (openrlhf/trainer/ppo_utils/experience_maker.py), the current dispatch order is:
# Reward model
r_refs = self._dispatch_forward(
self.reward_model_group,
args.train.colocate_all, # sync only if colocate_all
...
)
# Actor model
action_log_probs_ref = self._dispatch_forward(
self.actor_model_group,
args.train.colocate_all or args.train.colocate_actor_ref, # sync if colocate_actor_ref!
...
)
# Critic model
if args.train.colocate_critic_reward and r_refs is not None:
ray.get(r_refs) # explicit barrier for reward
ray.get(self.reward_model_group.async_run_method(method_name="empty_cache"))
value_ref = self._dispatch_forward(self.critic_model_group, ...)
# Reference model
base_action_log_probs_ref = self._dispatch_forward(
self.initial_model_group,
args.train.colocate_all or args.train.colocate_actor_ref,
...
)
where _dispatch_forward is:
def _dispatch_forward(self, group, sync_condition, **kwargs):
ref = group.async_run_method_batch(method_name="forward", **kwargs)
if sync_condition:
ray.get(ref) # blocks driver here
ray.get(group.async_run_method(method_name="empty_cache"))
return ref
This causes two distinct serialization issues:
Issue 1: colocate_actor_ref=True blocks Critic dispatch
When --train.colocate_actor_ref=True, _dispatch_forward calls ray.get(action_log_probs_ref) synchronously before returning. The driver is blocked until the Actor forward completes, so Critic dispatch is delayed even though Critic and Actor run on separate GPUs and could overlap.
Timeline with colocate_actor_ref=True:
Reward GPU: [=== reward forward ===] (async, not waited)
Actor GPU: [=== actor forward ===] (sync, driver waits)
Critic GPU: [=== critic forward ===] ← delayed!
Ref GPU: [=== ref forward ===] ← further delayed!
Issue 2: colocate_critic_reward=True blocks Reference dispatch
When --train.colocate_critic_reward=True (without colocate_all), the explicit ray.get(r_refs) before Critic also delays the Reference model dispatch, even though Reference runs on a separate GPU and could overlap with both Reward and Critic.
Timeline with colocate_critic_reward=True:
Reward GPU: [=== reward forward ===]
Actor GPU: [=== actor forward ===] (async)
Critic GPU: [=== critic forward ===]
Ref GPU: [=== ref forward ===] ← delayed by reward barrier!
Proposed Fix
Separate the sync barriers from the dispatch calls. Dispatch all models first, then sync as required by colocation constraints:
# Batch 1: dispatch Reward + Actor immediately (always parallelizable)
r_refs = _dispatch_forward(reward_model_group, sync_condition=colocate_all, ...)
action_log_probs_ref = _dispatch_forward(actor_model_group, sync_condition=colocate_all, ...)
# Sync barriers (only when GPU memory must be freed before next dispatch)
if colocate_actor_ref and not colocate_all:
ray.get(action_log_probs_ref)
ray.get(actor_model_group.async_run_method("empty_cache"))
if colocate_critic_reward and not colocate_all and r_refs is not None:
ray.get(r_refs)
ray.get(reward_model_group.async_run_method("empty_cache"))
# Batch 2: dispatch Critic + Reference (parallelizable with each other)
value_ref = _dispatch_forward(critic_model_group, sync_condition=colocate_all, ...)
base_action_log_probs_ref = _dispatch_forward(reference_model_group, sync_condition=colocate_all, ...)
Expected timeline:
Reward GPU: [=== reward ===]
Actor GPU: [=== actor ===] (barrier if colocate_actor_ref)
Critic GPU: [=== critic ===]
Ref GPU: [=== ref ===] ← now overlaps with Critic!
Benefits
- No changes to
_dispatch_forwardsignature - Correctly handles
colocate_actor_ref+colocate_critic_rewardcombined mode - Critic and Reference always overlap when running on separate GPUs
Affected Configurations
--train.colocate_actor_ref(withoutcolocate_all)--train.colocate_critic_reward(withoutcolocate_all)- Both flags combined
Would appreciate confirmation on whether this analysis is correct before submitting a PR.
- Dominant language
- Python
- Stars
- 10.1k
- Forks
- 1k
- Avg merge
- 5h 19m
- Merged PRs (30d)
- 4
Getting set up
This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from OpenRLHF/OpenRLHF
-
\fbox{} answers never extract — math reward is always 0 for fbox responsesPossibly taken @dhvani7014 claimed this 2 days ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Maintainers usually reply within 1 day
-
PPO critic averages LR and grad-norm telemetry across training stepsPossibly taken @Madhustat claimed this 14 days ago. Open
Difficulty 3/5 1-2 days Newbie friendliness 30/100
Maintainers usually reply within 1 day
-
Difficulty 4/5 3-5 days Newbie friendliness 40/100
Maintainers usually reply within 1 day
-
math_utils.grade_answer ignores units when both reference and prediction carry one, so the right number with the wrong unit (or wrong rate) is graded correctPossibly taken @MaybeIcanShow claimed this 21 days ago. Open
Difficulty 3/5 Half a day Newbie friendliness 35/100
Maintainers usually reply within 1 day
-
LoRA + colocated vLLM: policy→vLLM weight sync fails end-to-end (dtype, then unmapped adapter names)Possibly taken @Madhustat claimed this 23 days ago. Open
Difficulty 5/5 Over a week Newbie friendliness 15/100
Maintainers usually reply within 1 day
All issues in OpenRLHF/OpenRLHF
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
PolicyEngine/policyengine-us#10262 ·
Maintainers usually reply within 2 days
-
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
kellyjonbrazil/jc#788 ·
Maintainers usually reply within 2 days
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Maintainers usually reply within 1 day
-
`/search.json` ignores the documented `editions.sort` parameterPossibly taken @PranavD2905 claimed this today. OpenAffects: Server Good First Issue Module: Solr Needs: Triage Theme: Public APIs Theme: Search
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
internetarchive/openlibrary#13989 · 1 comment ·
Maintainers usually reply within 1 day
-
bug P2: misleading query
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
jeffknupp/association#478 ·