You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
After upgrading veRL v0.8.0 → v0.9.0, we observed a colocated GRPO regression on 8×H200/Qwen3-8B (V0 on both releases, identical Hydra overrides):
v0.8.0
v0.9.0
Change
Median weight sync
2.706 s
5.166 s
+90.9%
Median throughput
385.68 tok/s
279.49 tok/s
−27.5%
v0.9 added aggressive_empty_cache(force_sync=True) immediately before resuming rollout weights. Removing only that call from v0.9 saved 1.133 s (about 46% of the release gap); adding it to v0.8 cost 0.695 s. The release locks also differ, so the call is a material contributor, not the entire regression.
Why cleanup exists
PyTorch and vLLM are separate colocated processes. PyTorch must return unused reserved allocator blocks before vLLM remaps weights. empty_cache() performs that handoff without freeing live tensors.
The aggressive helper also runs full Python GC, synchronizes, and retries after releasing ≥1 GiB. PR #7242 added this call in an unrelated DeepSeek-V4 change without explaining it; its placement suggests conservative memory-headroom protection.
Native-V1 A/B (veRL v0.9.0)
Qwen3 workload
Aggressive sync
Plain sync
Improvement
Peak allocated (aggr./plain)
Peak reserved (aggr./plain)
8B dense
4.867 s
4.207 s
13.6%
45.671 / 45.685 GiB
53.121 / 53.180 GiB
8B param offload
3.731 s
3.031 s
18.8%
45.328 / 45.322 GiB
52.816 / 52.953 GiB
30B-A3B MoE
8.204 s
7.086 s
13.6%
71.245 / 71.245 GiB
74.393 / 74.393 GiB
Medians exclude cold step 1 and deliberate calibration steps 3–4. All six runs completed without OOM. Phase traces show:
First empty_cache() released 7.66–40.55 GiB/rank and remains necessary.
GC released zero CUDA bytes but cost 0.56–0.69 s.
Retry triggered on every measured rank/step, released zero additional cache, and cost another 0.55–0.63 s.
Explicit synchronization after empty_cache() was <0.8 ms.
Synthetic tests confirm GC can help when unreachable Python cycles own CUDA tensors; that condition was not observed in these natural traces.
Change only this pre-resume call. Keep the separate post-transfer aggressive cleanup before KV-cache resume. PR #7780 changes sender cleanup at another boundary and is not a duplicate; no overlapping open PR was found.
Activity
wuxibin89 commentedon Sep 14, 2026
@lbaolin The proposed change looks good to me, could you help to submit a PR?
lbaolin commentedon Sep 14, 2026
Yes, thanks, created the PR now
[rollout, perf] fix: avoid full GC before weight resume (verl-project…
[perf] fix: remove full gc during weight refit (#7873)