Skip to content

【Task 22】perf(hybrid): 降低纯文本 Hybrid-async 权重发布开销 - #211

Open
MaybeIcanShow wants to merge 26 commits into
redai-infra:mainfrom
MaybeIcanShow:perf/task22-hybrid-async-zero-kl
Open

【Task 22】perf(hybrid): 降低纯文本 Hybrid-async 权重发布开销#211
MaybeIcanShow wants to merge 26 commits into
redai-infra:mainfrom
MaybeIcanShow:perf/task22-hybrid-async-zero-kl

Conversation

@MaybeIcanShow

@MaybeIcanShow MaybeIcanShow commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

What

本 PR 完成贡献者计划 Task 22「Hybrid-async 纯文本性能」,并补齐最新评审提出的配置初始化与 Rollout 恢复问题:

  • kl_loss_coef == 0 时自动关闭无效的 KL/reference forward 及对应资源;
  • Hybrid 支持按 --update-weights-interval 2 每两次 Actor update 发布一次 Rollout 权重;
  • global dataset 的 num_rollout_per_epoch 在所有 service 创建前解析并注入 Actor;
  • non-global dataset 不再错误读取 epoch 长度,评测只按 eval_interval 触发发布;
  • pause 结果不确定或 recover 失败时执行幂等 resume;resume 最多重试 3 次,失败时所有 rank 一致中止;
  • normal Hybrid 和 fully-async 权重更新异常都会补偿 resume,并同步失败状态;
  • 本 PR 涉及的 health、evaluate 和 resume 请求使用可配置的 rollout_http_timeoutrecover_rollout_engines 使用面向引擎重建的较长 rollout_engine_init_timeout。这些控制面 HTTP 调用均采用有限超时,不再保留上游无固定 timeout 的语义;
  • benchmark 工具、固定数据和对应测试不进入 PR,改由个人 fork release 以源码包链接交付。

Closes #124.

Why

Hybrid-async 权重发布包含 Rollout pause、在途请求收敛、权重传输和 resume 的固定成本。小模型单步训练较短时,逐 step 发布容易形成流水空泡。interval-two 让两个 Actor update 共同摊销一次发布开销,代价是 Rollout 最多额外使用一次 Actor update 之前的权重。

kl_loss_coef == 0 时 KL 项严格为零,自动去掉 reference forward 不改变标量目标,但能避免无效计算和资源初始化。

Scope And Compatibility

  • 复用已有 --update-weights-interval,无新增或重命名 CLI/API;
  • 默认值仍为 1,现有 Hybrid recipe 保持逐 step 发布;
  • global dataset 保留 eval interval 和 epoch boundary 强制发布;
  • non-global dataset 没有 epoch boundary,只在 eval_interval、正常 interval 和最后一步发布;
  • TASK22_VARIANT=zero_kl UPDATE_WEIGHTS_INTERVAL=1 可关闭 interval-two 优化;
  • Rollout resume 失败不再被吞掉,避免训练继续后停在 collective;
  • rank 0 在权重更新函数内部的既有 Ray/锁异常仍可能导致 rank 分叉。该模式在 Task22 merge-base 0bc99af8 中已经存在,fully-async 锁流程可追溯至初始提交 eb15c45,不纳入本 PR 的通用分布式控制面重构。

Experiment

  • 上游合并提交:90d9adb2(合入 main@d52cd0a 并完成语义化冲突解析);
  • 规范清理提交:de5fd69e(benchmark 源码/数据/测试移至个人 fork release,测试按职责拆分);
  • 核心修复提交:946a0dc6aeaed3eacc4e763c024bf8d8c898b9c4;本次复现实验提交:6a70677ddea0416f76b026a7669618a1f7eca27b
  • 证据工具提交:6d2d71b03a453a958686417428e0eaf1789cfa91
  • 模型:Qwen3-0.6B;硬件:2× NVIDIA RTX PRO 6000 Blackwell,Actor/Rollout 各 1 GPU;
  • 数据:ModelScope GSM8K main 固定 16 prompts;
  • 每个配置 3 次串行运行,每次 20 steps;稳定窗口为 step 5-15;
  • 每 step 8 prompts × 4 samples,有效 batch 32,response cap 512;
  • train/log-prob token budget、采样参数、配对 seed、weight buffer 和 max staleness 固定。

baseline 传入原 zero-KL/reference 参数,由代码自动关闭 reference;zero_kl 显式不传这些参数。两组是配置等价性和运行噪声对照,主验收对比为 optimized 相对 zero_kl

Result

变体 响应 tok/s samples/s framework/E2E step (s) 发布耗时/step (s) 整体 GPU 利用率 Actor 峰值 MiB
baseline 4462.2 8.720 3.670 / 3.636 0.665 57.22% 43974
zero_kl 4395.3 8.594 3.724 / 3.758 0.713 54.43% 43974
optimized 5048.5 9.867 3.243 / 3.333 0.314 62.61% 51168
  • 自动/显式 zero-KL 吞吐差异:-1.50%,属于噪声对照;
  • interval-two 相对 zero_kl:响应吞吐 +14.86%,E2E 延迟 -11.29%
  • 三次配对 interval-two 吞吐提升:+14.52% / +13.77% / +16.33%
  • optimized 相对 baseline:响应吞吐 +13.14%,E2E 延迟 -8.33%
  • 最终 9 个证据作业均完成 20 steps,每个生成 640 samples;
  • 意外 NaN/Inf、runtime error、OOM 和样本丢失均为 0;
  • 所有变体均无 reference forward;interval-one/interval-two 每个作业发布 21/11 次,含初始化发布。

验收结论:通过,三次 optimized / zero_kl 配对结果均超过 5% 目标。

Memory Explanation

optimized Actor 峰值显存增加 7194 MiB。日志显示发布后 allocated/reserved 均约为 10.07/14.91 GiB;interval-two 跳过的 step 不执行 print_memory(..., clear_before_print=True),下次发布前 reserved 从约 37.5 GiB 增至约 44.6 GiB。增量是可回收的 PyTorch allocator cache 高水位,不是 live tensor、模型或 batch 增大;下次发布后回落到约 14.91 GiB。

Evidence

Testing

bash -n scripts/training/text/run-qwen3-0.6B-2xgpu-hybrid-async.sh
python -m pytest -q \
  tests/backends/megatron/test_hybrid_weight_publication.py \
  tests/components/test_rollout_weight_update_coordination.py \
  tests/core/test_controller_rollout_bootstrap.py \
  tests/distributed/ray/test_rollout_manager_bootstrap.py \
  tests/engine/rollout/test_bootstrap.py \
  tests/utils/test_arguments_opd_teacher_colocate.py \
  tests/utils/test_arguments_zero_kl.py
  • 合并定向回归:22 passed, 4 skipped(缺少可选 Megatron 依赖时显式 skip);arguments 扩展测试:18 passed;HTTP timeout/resume fixtures:30 passed
  • Ruff lint/format、compileall、shell 语法和 git diff --check:通过;
  • PR 相对 main 不包含 benchmarks/tests/benchmarks/.gitignore 或 pre-commit 配置改动;
  • release benchmark 源码包与 GitHub asset digest 一致。

Correctness Boundary

这是短周期性能实验。大部分 step 的 DAPO reward 没有组内方差,不能据此证明 interval-two 与 interval-one 的长期收敛等价。结论不外推到 Qwen3-4B/8 GPU。

MaybeIcanShow and others added 10 commits August 2, 2026 08:01
# ⚡ Performance

## Tune weight publication chunking

- Add a reproducible two-GPU Qwen3-0.6B Hybrid-async recipe with a checked-in tiny dataset.
- Compare the unchanged reference/KL workload at 512 MiB and 1 GiB weight-update buffers across three paired trials.

---

# ✅ Tests

- Add analyzer tests for metric disambiguation, completion intervals, GPU windows, and non-finite guards.

---

# 📝 Documentation

- Document fixed workload, environment capture, reproduction, rollback, and report generation.
# ⚡ Performance

## Add staged Hybrid-async comparisons

- Isolate the unused zero-KL reference path from batching gains
- Tune train and log-prob token budgets independently
- Fix formal runs at 20 steps and measure logged steps 5-15

---

# ✅ Tests

## Guard the benchmark analysis

- Validate stable-window, GPU-memory, and microbatch parsing
- Enforce fixed workload, paired seeds, and variant manifests

---

# 📝 Documentation

## Document acceptance and rollback

- Freeze a five-percent throughput target
- Record three-way trial ordering and raw artifact paths
# ⚡ Performance

## Record the three-stage batching experiment

- Compare the original baseline, zero-KL fix, and role-specific token budgets
- Preserve three paired 20-step trials with metrics from steps 5 through 15
- Document the rejected token-budget direction and its actor-train regression

---

# 🐛 Bug Fix

## Generate portable benchmark CSV files

- Use LF line endings so generated results pass repository whitespace checks
# ⚡ Performance

## Honor weight publication intervals in Hybrid mode

- Publish rollout weights at configured completed-step boundaries
- Skip rollout pause and resume coordination when publication is omitted
- Force publication on the final step and before configured evaluation

## Define the Task 22 interval-two experiment

- Compare baseline, zero-KL fix, and interval-two publication
- Keep token budgets, buffer size, workload, and max staleness fixed
- Preserve rejected buffer-size and token-budget directions in the report

---

# ✅ Tests

## Cover Hybrid publication boundaries

- Verify interval-one compatibility, interval-two cadence, and final sync
- Verify conservative evaluation sync and invalid interval handling
# ⚡ Performance

## Record the formal three-stage benchmark

- Compare original baseline, zero-KL fix, and interval-two publication
- Report a 13.48 percent throughput gain for interval two over zero-KL
- Preserve three paired trials and step 5 through 15 measurements

---

# ✅ Tests

## Strengthen result validation

- Require 20 complete steps and 640 samples in every component run
- Validate observed weight-publication counts against each configured interval
- Report TIS, clipping, non-finite values, and runtime-error guardrails

Co-authored-by: zheself <3010086230@qq.com>
Co-authored-by: zheself <3010086230@qq.com>
MaybeIcanShow and others added 3 commits August 2, 2026 14:08
Co-authored-by: zheself <3010086230@qq.com>
Co-authored-by: zheself <3010086230@qq.com>
Co-authored-by: zheself <3010086230@qq.com>
@MaybeIcanShow

Copy link
Copy Markdown
Contributor Author

@MaybeIcanShow

Copy link
Copy Markdown
Contributor Author

Task 22 实验附件说明

本附件为 Task 22 三组配对实验的完整交付包 task22-hybrid-async-text.zip,共包含 10 个文件:

文件 内容
report.md 中文实验报告:优化动机、理论解释、结果表、方法、正确性护栏、复现与回退说明
summary.csv 9 次组件运行的汇总指标,包括吞吐、step 耗时、GPU 利用率、峰值显存和 TIS
step_metrics.csv 稳定窗口 step 5-15 的逐 step 指标,包括吞吐、framework step、训练等待和权重发布耗时
throughput_curves.svg 三组变体逐 step 响应吞吐曲线,含横纵坐标、数值刻度、网格和运行分隔线
step_time_curves.svg 三组变体 framework step 耗时曲线
weight_update_curves.svg 三组变体前序权重发布耗时曲线,用于展示 interval-two 的开销变化
raw-evidence.tar.gz 3 个变体 × 3 次运行的脱敏原始日志、manifest、提交记录和 GPU 采样
raw-evidence-index.csv 原始文件与脱敏交付文件的逐文件 SHA-256 索引
raw-evidence.sha256 脱敏原始证据包校验值

主要结论

  • zero_kl 相对 baseline 的响应吞吐提升 +6.05%
  • optimizedupdate-weights-interval=2)相对 zero_kl 提升 +13.48%,达到 Task 22 至少 5% 的验收目标;
  • optimized 相对 baseline 的总吞吐提升 +20.34%,端到端延迟下降 -16.28%
  • 9 个作业均完成 20 steps,每个作业生成 640 samples;意外 NaN/Inf、运行时错误、OOM 和样本丢失均为 0;
  • optimized 的 Actor 峰值显存增加 7190 MiB,已在报告中作为性能收益的显存代价记录。

校验值

task22-hybrid-async-text.zip
SHA-256: 7b68b08a87429348590a94100277eb9c79c5140b31923ae3f89618efa160c694

raw-evidence.tar.gz
SHA-256: 8cf759a2fbed8baacf149985881d66aac71a4ab92066b523caf5790ee2568237

复现与边界

附件实验固定 Qwen3-0.6B、2× NVIDIA RTX PRO 6000 Blackwell、16 条 GSM8K 数据、有效 batch 32、response cap 512 和 step 5-15 稳定窗口。该实验只证明固定 2 GPU 小模型配置下的短周期性能收益,不外推到 Qwen3-4B/8 GPU,也不证明 interval-two 与 interval-one 的长期收敛等价。

复现命令:

MODEL_PATH=/path/to/Qwen3-0.6B CUDA_VISIBLE_DEVICES=2,3 TOTAL_TRIALS=3 \
  bash benchmarks/task22_hybrid_async_text/run_paired_trials.sh

@Michael-Salon

Copy link
Copy Markdown

实现方面可能有以下问题:

  1. zero_kl 的 6.05% 建议做成代码,而不是只留在 recipe 里
    建议在代码里识别 kl_loss_coef == 0 并跳过 ref forward。这样 6.05% 对所有人自动生效,不需要用户自己去删参数。
  2. +7190 MiB 的显存增长需要解释
  3. 配置 eval 时 interval 优化会静默失效
    最低限度请在降级分支加 logger.warning;彻底方案可仿照 SFT 路径(controller.py:102 + engine/sft/bootstrap.py)在创建 service 前把该值解析进 config。
  4. 被删除的注释里有一条死锁警告,希望补充说明
    PR 替换为「Skipped publications must leave rollout progressing」,结论相反但未给论证。

@MaybeIcanShow

Copy link
Copy Markdown
Contributor Author

Task 22 评审修复与 v4 复测

已在 a0b16761cd8df2828cba08e05b5c861fef7924da 完成评审意见修复,并在原条件下串行重跑 3 变体 × 3 次配对实验。

对评审意见的处理

  1. kl_loss_coef == 0:已改为代码自动关闭 KL/reference 路径及未使用资源,不再要求用户手动删 recipe 参数。
  2. +7194 MiB:来自 interval-two 跳过 step 未清理的 PyTorch reserved allocator cache 高水位,不是 live tensor 或 batch/模型增大;下次发布后 reserved 会回落。
  3. eval 降级:已在 interval 被评测边界降级时记录一次 logger.warning
  4. 死锁/协议说明:已补充 pause/swap/resume 顺序和跳过发布的不变式;同时在 eval/更新失败时尝试 resume,pause 准备失败时回滚内部状态。

复测结果

baseline 传入原 zero-KL/reference 参数并由代码自动关闭 reference,zero_kl 显式关闭;两者是等价性/噪声对照。

变体 响应 tok/s samples/s 发布耗时/step Actor 峰值 MiB
baseline 4449.5 8.701 0.673 s 43974
zero_kl 4484.9 8.767 0.665 s 43974
optimized 5071.9 9.910 0.304 s 51168
  • baseline / zero_kl 吞吐差异 +0.80%
  • optimized / zero_kl 吞吐提升 +13.09%,三次配对分别为 +13.52% / +13.39% / +12.36%
  • E2E 延迟相对 zero_kl 下降 10.74%
  • 9/9 作业完成 20 steps,每作业 640 samples,无意外 NaN/Inf、runtime error、OOM 或样本丢失;
  • 所有变体 has_reference_forward=false;interval-one/interval-two 发布次数为 21/11。

新证据:Task 22 v4 release assets,报告、CSV、SVG 曲线和原始证据以 9 个独立 asset 发布。

  • raw-evidence.tar.gz SHA-256:da497ee37d05f97e43c5c6277b21dd7793ee0c4236587d4fc60e054f78674ff6
  • 验证:pre-commit 通过;Task 22 定向测试 29 passed, 1 skipped(可选 Megatron 依赖不可用);release asset 校验通过。

@Michael-Salon

Copy link
Copy Markdown

以下两个问题可以检查下:

  1. interval 优化在配 eval 时实际不生效:Actor 侧 num_rollout_per_epoch 恒为 None(只在 Rollout 进程解析),会走"每步发布"回退。只加 warning 不够,应像 SFT 那样在建 service 前解析进 config 并注入 Actor args。

  2. rollout_unavailable 路径漏了 resume(会死锁):_check_services_health 里 pause 已成功、但 recover 失败时,train_hybrid 会跳过 end_update_weight,rollout 卡死在 paused。pause 一旦成功就应保证补发 resume。

@MaybeIcanShow

MaybeIcanShow commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

最新评审修复与复测
已推送 946a0dc(核心修复)和 6d2d71b(证据工具修复),并按原固定配置完成复测。

评审问题处理
Controller 在创建任何 Actor/Rollout service 前解析 global dataset 的 num_rollout_per_epoch,覆盖 fully-async 并行创建分支;
non-global dataset 不再调用仅支持 global dataset 的 epoch 长度接口,eval 只按 eval_interval 强制发布;
pause 请求发出后,响应丢失或 recover 失败都会补偿幂等 resume;
resume 最多重试 3 次,URL discovery 也在重试循环内,失败后所有 rank 一致抛错;
normal Hybrid 和 fully-async 更新异常均补偿 resume,并同步更新失败状态;
health/recover/evaluate/resume HTTP 请求补齐 timeout;

复测结果

变体 响应 tok/s framework step 发布耗时/step
baseline 4465.7 3.667 s 0.673 s
zero_kl 4431.7 3.692 s 0.680 s
optimized 5039.1 3.249 s 0.318 s

optimized / zero_kl:+13.71%;三次配对为 +14.10% / +13.00% / +14.03%;
最终 9/9 作业完成 20 steps,每作业 640 samples;
无意外 NaN/Inf、runtime error、OOM 或样本丢失;
定向测试 42 passed,pre-commit 和证据 SHA-256 校验通过。

证据:Task 22 v6 release

raw-evidence.tar.gz SHA-256:740f39d14c993d9240d646ddb6600ceff9c17c12810689ba765134f6c8efe2cf。

@Michael-Salon

Copy link
Copy Markdown

以下两个问题可以参考下,解决完建议合入:

#1 pause_requested 置位过早,把可降级的 rollout 崩溃升级成全作业致命错误

位置:relax/backends/megatron/actor.py:1842(置位)、:1856-1861(补偿)、:1897-1898(抛错)

while True:
pause_requested = True # ← 在 requests.get 之前
response = requests.get(f"{rollout_serve_url}/can_do_update_weight_for_async", ...)

ConnectionError(进程已死、端口拒绝)能确定服务端没收到请求,rollout 不可能处于 paused,但这里一律按"pause 结果不确定"处理去发补偿 resume。而 rollout 已死 ⇒ 补偿必然失败 ⇒ rollout_resume_failed=True ⇒ 全
rank raise RuntimeError。

回归点:改之前同一场景走 actor_fwd_only = True,日志明写 "Will continue without rollout update for this step",训练继续、由 health manager 拉起 rollout。现在任何 rollout
服务崩溃都会杀掉整个作业,--use-fault-tolerance 失效。

附带代价:_end_rollout_weight_update 重试 3 次 × rollout_http_timeout(默认 120 s)+ 2 s sleep = 最坏约 362 s。这期间 rank 0 卡在 except 里,其余 rank 全部堵在下面的 dist.all_reduce 干等。

建议改法:

  1. pause_requested = True 挪到拿到 response 之后(或至少排除 ConnectionError);
  2. 只有 pause 被确认过(res 为真、走到过 break)才把 resume 失败升级为 RuntimeError;未确认的情况保留原来的 actor_fwd_only=True 降级语义。

现有测试 test_hybrid_weight_publication_does_not_resume_before_pause_request 只覆盖了 get_serve_url 抛错,没覆盖 connection refused,建议补一条。


#2 Actor 的"发布判据"与 Rollout 的"评测判据"条件不同,靠隐式耦合才一致

位置:actor.py:108-136(_should_publish_hybrid_weights)vs relax/components/rollout.py:360-370(_should_eval)

  • Actor:epoch 边界这一支由 evaluation_at_epoch_boundary = args.rollout_global_dataset 控制(调用点 actor.py:1514-1523)
  • Rollout:(step % eval_interval == 0) or (self.num_rollout_per_epoch is not None and step % self.num_rollout_per_epoch == 0) —— 完全不看 rollout_global_dataset

两者今天结论相同,只因为本 PR 在两个不同地方分别把非 global dataset 的 num_rollout_per_epoch 强制成 None(engine/rollout/bootstrap.py:23 和 distributed/ray/placement_group.py:120)。任何一处将来漂了,Actor
就会在 Rollout 真要评测的那一步跳过发布 —— 评测静默跑在旧权重上,不报错、不告警。

建议改法:直接复用已有的 relax/utils/misc.py:91-111 should_run_periodic_action(rollout_id, interval, num_rollout_per_epoch, num_rollout) —— 它的 step = rollout_id + 1、(step % interval == 0) or (nrpe is
not None and step % nrpe == 0)、以及"最后一步强制触发"都跟这里逐字相同。复用之后 evaluation_at_epoch_boundary 这个参数可以整个删掉,耦合从构造上消失。

顺带解决另外两点:_should_publish_hybrid_weights 不再重复实现公共 helper;train_hybrid 里 _wait_for_previous_eval() 被挪进 if should_publish_weights: 之后所依赖的"发布判据 ⊇
评测判据"不变式也就有了代码层面的保证。

@Michael-Salon

Copy link
Copy Markdown

对于rollout的修改,想确认一点:

_should_eval 现在会在最后一步强制评测,影响所有 RL 模式(relax/components/rollout.py:365)

新代码给 should_run_periodic_action 传了第 4 个参数 self.config.num_rollout,该 helper 内含:

if num_rollout is not None and rollout_id == num_rollout - 1:
return True # ← 最后一步无条件触发

旧 _should_eval 没有这条。举例 eval_interval=10, num_rollout=25:旧的在 rollout_id 9/19 评测,新的额外在 24 也评。而 _should_eval 是 Rollout 组件的公共方法,sync colocate / fully_async / hybrid 都走它 ——
等于给所有现有 RL recipe 末尾多加一次 eval pass。

新测试没有覆盖到这个变化:test_rollout_evaluation_uses_periodic_and_epoch_boundaries 用 eval_interval=10, nrpe=4, num_rollout=20,_should_eval(19) 走 20 % 10 == 0
就已经为真,分不出是不是最后一步强制触发的。

# 🐛 Bug Fix

## Preserve RL evaluation cadence

- Keep rollout evaluation limited to configured interval and epoch boundaries
- Retain final-step weight publication without forcing final-step evaluation
- Document the optional final-trigger semantics of num_rollout
- Align final-step evaluation comments across RL training modes

---

# ✅ Tests

## Guard final-step behavior

- Cover a non-periodic final rollout that must not trigger evaluation
- Verify Hybrid publication still covers every actual evaluation boundary
@Michael-Salon
Michael-Salon marked this pull request as ready for review August 10, 2026 04:21
@Michael-Salon

Michael-Salon commented Aug 10, 2026

Copy link
Copy Markdown

✅ Acceptance passed
效果(Qwen3-0.6B,2 GPU)

  • optimized 相对 zero_kl:吞吐 +13.54%(三轮 +12.71/13.57/14.38%)
  • optimized 相对 baseline:吞吐 +12.74%
  • E2E step 延迟降低约 10%
  • 9/9 作业成功,0 runtime error / OOM / NaN,均超过 5% 验收目标
  • 代价:Actor 峰值显存 +7GB 左右(可回收的 allocator cache 高水位,非真实显存增长)

Copilot AI lite review requested due to automatic review settings August 11, 2026 04:45

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR optimizes Hybrid-async (text) training by reducing unnecessary reference/KL work when kl_loss_coef == 0, adding interval-based Hybrid weight publication, and hardening rollout pause/recover/resume coordination so failures don’t silently leave the system in an inconsistent state.

Changes:

  • Auto-disable --use-kl-loss when --kl-loss-coef == 0 and drop unused reference resources.
  • Introduce Hybrid weight publication interval logic and align publication with eval/epoch boundaries and final-step forcing.
  • Resolve RL num_rollout_per_epoch / num_rollout on the controller before services are created; add focused regression tests for sizing + pause/resume behaviors.

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
relax/utils/arguments.py Normalize zero-KL settings early and drop unused reference resources.
relax/utils/misc.py Extend periodic-trigger helper to optionally force the final rollout.
relax/engine/rollout/bootstrap.py New controller-side RL rollout sizing helper.
relax/core/controller.py Resolve RL rollout sizing before service creation (incl. fully-async parallel creation).
relax/distributed/ray/placement_group.py Avoid querying epoch sizing for non-global datasets; respect controller pre-resolution.
relax/components/rollout.py Harden pause/prepare handshake for weight updates.
relax/backends/megatron/actor.py Add interval-based Hybrid publication, resume retries, and synchronized failure handling.
relax/engine/sft/bootstrap.py Update docstring to reflect RL sizing location.
scripts/training/text/run-qwen3-0.6B-2xgpu-hybrid-async.sh Add Task 22 2-GPU Hybrid-async recipe with variants.
tests/utils/conftest.py Centralize arguments_module fixture.
tests/utils/test_arguments_zero_kl.py New tests for zero-KL normalization and reference resource dropping.
tests/utils/test_arguments_opd_teacher_colocate.py Ensure zero-KL normalization happens before reference checkpoint validation.
tests/engine/rollout/test_bootstrap.py New tests for RL rollout sizing resolution behavior.
tests/distributed/ray/test_rollout_manager_bootstrap.py Ensure non-global datasets don’t query epoch length during RolloutManager bootstrap.
tests/core/test_controller_rollout_bootstrap.py Ensure controller resolves epoch sizing before actor/rollout services are created.
tests/components/test_rollout_weight_update_coordination.py Add tests for eval boundary logic and rollback on weight-update prepare failure.
tests/backends/megatron/test_hybrid_weight_publication.py New tests for interval publishing + resume/retry + failure synchronization.
tests/backends/megatron/test_actor_http_timeout.py Adjust tests to reflect new resume collective behavior and cleanup ordering.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread relax/backends/megatron/actor.py Outdated
Comment thread relax/components/rollout.py Outdated
Copilot AI review requested due to automatic review settings August 11, 2026 06:28
Restore health monitoring when weight-update preparation fails after pause succeeds, and cover the rollback with a regression assertion.

The separate handling for an uncertain pause result remains aligned with Michael-Salon's recommendation: only a confirmed pause with failed resume escalates to a synchronized job failure; a pause that is proven not to have been sent keeps the degradation path.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 18 out of 18 changed files in this pull request and generated no new comments.

Suppressed comments (2)

relax/engine/rollout/bootstrap.py:44

  • resolve_rl_num_rollout relies on assert for user/config validation (dataset size vs batch size, and resolved num_rollout positivity). Asserts can be disabled with python -O, which would allow invalid configs to slip through and later fail in less obvious ways. Prefer raising ValueError (or RuntimeError) for these validation checks.
    dataset_size = ray.get(data_source.lengths.remote())
    num_per_epoch = dataset_size // config.rollout_batch_size
    assert num_per_epoch > 0, f"Dataset size {dataset_size} < rollout_batch_size {config.rollout_batch_size}"

    config.num_rollout_per_epoch = num_per_epoch
    if config.num_epoch is not None:
        epoch_rollout = num_per_epoch * config.num_epoch
        config.num_rollout = (
            min(config.num_rollout, epoch_rollout) if config.num_rollout is not None else epoch_rollout
        )
    assert config.num_rollout is not None and config.num_rollout > 0

relax/core/controller.py:418

  • resolve_rl_num_rollout() already resolves num_rollout_per_epoch and may clamp num_rollout with min(num_rollout, num_epoch * num_rollout_per_epoch). However, Controller.register_all_serve() still calls _maybe_resolve_num_rollout(roles_to_create) afterwards, which re-queries data_source.lengths and overwrites config.num_rollout as num_epoch * num_rollout_per_epoch unconditionally (dropping any user-provided --num-rollout cap) and adds an extra Ray RPC. Consider either removing that second resolution for RL once num_rollout_per_epoch is set, or updating _maybe_resolve_num_rollout to preserve the existing min/clamp behavior.
                data_source = ray.remote(num_cpus=1)(data_source_cls).remote(self.config)
                # Actor and Rollout services are created in parallel for
                # fully-async training, so resolve epoch boundaries on the
                # controller before either service receives its config.
                resolve_rl_num_rollout(self.config, data_source)

@MaybeIcanShow

MaybeIcanShow commented Aug 11, 2026

Copy link
Copy Markdown
Contributor Author

Copilot Review 处理说明

1. pause 结果不确定时 resume 失败

本次暂不采用 Copilot 提出的“所有不确定状态都 fail-closed”方案。当前实现遵循 Michael-Salon review 的建议:

  • 能确认 pause 请求未发出(例如连接建立失败):保留降级路径,跳过本次 Rollout 权重更新并继续训练。
  • 已确认 pause 成功、但 resume 失败:通过 collective 同步失败状态,让所有 rank 一致终止。
  • pause 结果不确定:仍尝试补偿 resume;resume 失败时暂记录 warning 并继续降级。

Copilot 提出的风险已记录。若后续改用更保守的 fail-closed 策略,需要单独调整这一协议边界。

2. prepare 失败时未恢复 health monitoring

该问题已修复,提交为 4603e03

health_monitoring_pause() 成功、但 set_weight_updating(True) 失败时,现在会:

  • 回滚 set_weight_updating(False)
  • 调用 health_monitoring_resume()
  • 释放 handshake event;
  • 继续抛出原始 prepare 异常。

同时新增了回归断言,验证 health monitoring 必须恢复。

@Michael-Salon

Copy link
Copy Markdown

以下几个问题建议修改:

Copilot AI review requested due to automatic review settings August 11, 2026 16:56
@MaybeIcanShow

Copy link
Copy Markdown
Contributor Author

@Michael-Salon 最新 4 项意见已在 29a6a9b 修复并推送。

评审问题处理

  1. ref_load=None 的 checkpoint 回退

    • process_args 仅在 ref_load 非空时覆盖 ActorFwd/reference 的 args.load,保留 bridge 已解析出的 HF checkpoint 或有效 Actor checkpoint;
    • raw 模式缺少可用 Megatron load/ref_load 时提前抛出明确 ValueError,checkpoint loader 也增加了 load=None 防御;
    • 新增 actor_fwd/reference、bridge/raw/fallback 回归用例。
  2. pause 超时后的 late-pause 竞态

    • 每次 pause 使用 Actor session 内单调递增的 transaction ID,pause 与补偿 end 携带同一 ID;
    • Rollout 端按事务维护 tombstone、cancel、ready 和 end lock。即使补偿 end 先返回、原 pause 请求随后才执行,也会在 pause 完成后立即回滚,不会遗留 paused 状态;
    • 旧事务的迟到 end 不能恢复新事务;事务和 session retention 均有上限及过期回收;
    • 补充 end-before-pause、late old end、重复请求、并发 end、容量和过期回收等时序测试。
  3. interval-two 的 old log-prob 正确性

    • --hybrid --update-weights-interval > 1 现在强制要求有效修正路径;
    • 可使用 --use-tis--use-rollout-logprobs,或在 max_staleness=0 时使用 --keep-old-actor;true-on-policy 模式只接受 TIS;
    • 不满足条件会在启动阶段抛 ValueError,避免静默使用错误 behavior policy;Task 22 recipe 使用 --use-tis
  4. 短 global dataset 兼容性与错误类型

    • 显式设置正 num_rollout 且未使用 num_epoch 时,允许 dataset_size < rollout_batch_size,数据源继续跨 epoch 补齐 batch;
    • 此场景没有 step-aligned epoch boundary,因此 num_rollout_per_epoch=None
    • 空数据集、无有效 step 数及不成立的 num_epoch 配置均改为明确 ValueError,不再使用 assert
    • fixture 已从 tests/utils/conftest.py 迁移为以 test_ 开头的 test_arguments_helpers.py,新增测试文件同样符合命名要求。

v9 完整复测

在基于 4603e03 冻结的测试工作区上,按原固定配置串行执行 3 变体 x 3 轮配对实验:Qwen3-0.6B、物理 GPU 2/3、20 steps、8 prompts x 4 samples、稳定窗口 step 5-15。Release 附带完整 tested-worktree 快照和 provenance;29a6a9b 包含同一功能改动及提交前 docformatter 调整。

变体 响应 tok/s E2E step 发布耗时/step 发布次数
baseline 4406.9 3.702 s 0.693 s 21
zero_kl 4430.8 3.708 s 0.693 s 21
optimized 5002.4 3.402 s 0.336 s 11
  • optimized / zero_kl+12.90%,三轮配对分别为 +13.20% / +12.60% / +12.91%
  • optimized / baseline+13.51%
  • optimized / zero_kl E2E step:-8.26%
  • 9/9 正式作业成功,每个作业完成 20 steps、640 samples;runtime error 和意外 NaN/Inf 均为 0;
  • Actor 峰值显存 43974 -> 51168 MiB,增加 7194 MiB,仍是 interval-two allocator cache 高水位代价;
  • 首次尝试遇到 Ray runtime-env 打包竞态,属于基础设施失败,已排除在正式结果之外并单独保留。

证据:Task 22 v9 fork release完整报告

raw-evidence.tar.gz SHA-256:e76f53da3ff064da4045b9b539c9aebddeff5a959884ebb884092c1b39106d81。12 个 Release asset 已远端回读并逐项通过 SHA-256 校验。

验证

  • source relax.env 后定向测试:189 passed,0 skipped
  • ruff、ruff-format、docformatter、冲突标记、gitleaks、git diff --check 均通过;
  • 只读协议审查未发现阻止推送/合入的问题。

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 28 out of 28 changed files in this pull request and generated no new comments.

Suppressed comments (2)

relax/backends/megatron/actor.py:1870

  • PR description states long-running recovery should keep the upstream “no fixed timeout” semantics, but /recover_rollout_engines is now called with timeout=self.args.rollout_engine_init_timeout. If the intent is to introduce a finite recovery timeout (as tests suggest), the PR description should be updated; otherwise, remove the fixed timeout here to match the documented behavior.
                        response = requests.get(
                            f"{rollout_serve_url}/recover_rollout_engines",
                            timeout=self.args.rollout_engine_init_timeout,
                        )

relax/backends/megatron/actor.py:619

  • PR description says long-running /evaluate should keep the upstream “no fixed timeout” semantics, but this call now uses timeout=self.args.rollout_http_timeout, which can prematurely abort legitimately slow evaluations and contradicts the stated contract. Please either (a) update the PR description/any docs to match the new finite-timeout behavior, or (b) drop the fixed timeout here (and adjust tests accordingly).

This issue also appears on line 1867 of the same file.

                response = requests.get(
                    f"{rollout_serve_url}/evaluate",
                    params={"train_step": rollout_id},
                    timeout=self.args.rollout_http_timeout,
                )

@MaybeIcanShow

MaybeIcanShow commented Aug 11, 2026

Copy link
Copy Markdown
Contributor Author

关于 Copilot 最新 review 中两条 suppressed timeout 提示:已直接更新 PR 描述,使文档与 29a6a9b 的实际行为一致。

  • health、evaluate、resume 使用有限且可配置的 rollout_http_timeout
  • recover_rollout_engines 使用面向引擎重建的较长 rollout_engine_init_timeout
  • 已移除“evaluate/recover 保持上游无固定 timeout”的旧表述。

@Michael-Salon

Copy link
Copy Markdown

已复核当前提交 29a6a9b。上一轮提出的 checkpoint 回退、pause late-arrival、interval-two old log-prob 正确性以及短数据集兼容性问题均已修复,并有对应回归测试覆盖;新增测试文件命名也符合 test_*.py 约定。本轮未发现新的阻塞问题,LGTM

Copilot AI review requested due to automatic review settings August 12, 2026 04:22
@MaybeIcanShow
MaybeIcanShow requested a review from yxyOo as a code owner August 12, 2026 04:22

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings August 12, 2026 04:27

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Contributor Program Task 22] Profile and optimize text Hybrid-async on a low-resource Qwen3 setup

4 participants