Repository navigation
feat(automodel): make the process-group timeout configurable - #4876
linmuchuiyang wants to merge 3 commits into
Conversation
Add policy.automodel_cfg.distributed_timeout_minutes (and value.automodel_cfg for PPO), the Automodel-path counterpart of megatron_cfg.distributed_timeout_minutes (NVIDIA-NeMo#4481). setup_distributed passes timeout=datetime.timedelta(minutes=...) to torch.distributed.init_process_group, so the default group and the FSDP2 / expert-parallel groups derived from it use the configured collective timeout instead of torch's 10-minute NCCL default. Unset keeps the default; non-positive values are rejected with the same message pattern as NVIDIA-NeMo#4481. On the Automodel/DTensor path the first training step JIT-compiles kernels (HybridEP / DeepEP, TileLang) and loads large MoE checkpoints; on cold nodes that keeps ranks apart for more than 10 minutes, and until now the only remedy was patching setup.py. Signed-off-by: linmuchuiyang <6169320+linmuchuiyang@users.noreply.github.com>
dd0b3dd to
b02cbd1
Compare
sharonyu-115
left a comment
There was a problem hiding this comment.
Thanks for contributing this configurable timeout for Automodel training.
This PR adds automodel_cfg.distributed_timeout_minutes, including support for PPO value workers, to accommodate long initialization, kernel compilation, and checkpoint-loading delays. Unset or null preserves PyTorch’s default; non-positive values are rejected.
Three P2 issues need attention:
- C1: Forward the timeout to newly created training subgroups and add a regression test for that forwarding.
- C2: Update the five reference YAML snapshots affected by the new default.
- C3: Mark the new timeout tests so the Automodel CI lane selects them.
Validation used isolated CPU harnesses and pinned upstream source. The config failures and test-selection gap were reproduced; full repository tests and GPU/NCCL execution were not run.
Generated by Codex
| torch.distributed.init_process_group(backend=backend) | ||
| else: | ||
| torch.distributed.init_process_group( | ||
| backend=backend, timeout=datetime.timedelta(minutes=timeout_minutes) |
There was a problem hiding this comment.
C1 · 2 action items.
TL;DR — newly created training subgroups retain the backend timeout despite the new setting.
The default-group initialization receives the timeout, but the mesh construction call omits it. Pinned Automodel documents that mesh subgroups do not inherit this value, so those collectives can still time out during the long first-step delays this option targets.
AI-1
Pass timeout_minutes= with the result of _distributed_timeout_minutes(config) to the mesh construction call. Automodel already forwards this parameter. The call is outside this diff, so an in-place suggestion cannot cover it.
AI-2
Extend the existing distributed-setup test to check that an explicit timeout reaches both default-group initialization and mesh construction, and that absent/null settings preserve default behavior. Require the explicit-timeout case to fail before the fix and pass afterward. The new helper-only tests do not exercise mesh construction. The existing test is outside this diff, so an in-place suggestion cannot cover the edit.
| # Collective timeout (minutes) for the default process group and the FSDP2 / | ||
| # expert-parallel groups derived from it. null = torch default (10 for NCCL). | ||
| # Raise it when the first step JIT-compiles kernels (MoE dispatchers, TileLang) or I/O is slow. | ||
| distributed_timeout_minutes: null |
There was a problem hiding this comment.
C2 · 1 action item.
TL;DR — the new YAML defaults break five reference-config checks.
The added key is absent from the reference snapshots. An isolated run of the existing parity check, using the real config loader, reproduced five failures for missing timeout keys.
AI-1
Add the policy Automodel timeout default (null) to dpo.yaml, grpo_math_1B.yaml, rm.yaml, sft.yaml, and ppo_math_1B_megatron.yaml. Add the same default to PPO's value Automodel configuration. These snapshots are outside the diff, so an in-place suggestion cannot cover this change.
| ) | ||
|
|
||
|
|
||
| class TestDistributedTimeoutMinutes: |
There was a problem hiding this comment.
C3 · 1 action item.
TL;DR — the Automodel CI lane filters out all seven new timeout cases.
The new test class lacks the marker required by the Automodel lane and its collection filter. An isolated run with that filter selected none; adding the marker ran all seven successfully.
AI-1
Add the class marker:
| class TestDistributedTimeoutMinutes: | |
| @pytest.mark.automodel | |
| class TestDistributedTimeoutMinutes: |
Address the review on NVIDIA-NeMo#4876: - C1: pass timeout_minutes to MeshContext.build. DeviceMesh axes create their own NCCL groups and do not inherit the default group's timeout; Automodel applies the value through a per-axis backend override. Correct the _init_process_group docstring that claimed otherwise. - C1 test: TestSetupDistributed now checks that an explicit timeout reaches both init_process_group and MeshContext.build, and that null / absent keeps torch's default for both. The explicit case fails on the previous commit (assert None == 45) and passes with the fix. - C2: add the automodel_cfg.distributed_timeout_minutes default to the five reference config snapshots (policy and value for PPO), fixing the five test_reference_configs_up_to_date failures. - C3: mark TestDistributedTimeoutMinutes with @pytest.mark.automodel so the Automodel CI lane (--automodel-only) selects it. - Sort the test module's import block (ruff I001). Signed-off-by: linmuchuiyang <6169320+linmuchuiyang@users.noreply.github.com>
|
Thanks for the careful review — all three points were right, and they are addressed in bd9d948: C1 — timeout now reaches the mesh sub-groups. C2 — reference snapshots updated. C3 — tests are in the Automodel lane. Validation (CPU, Python 3.13 venv with torch 2.13 and the pinned Automodel submodule,
GPU / NCCL execution was not run here; the sub-group timeout path is exercised by Automodel's own |
sharonyu-115
left a comment
There was a problem hiding this comment.
@linmuchuiyang Thanks for addressing the review comments. A further review exposed another issue:
Blocked pending Automodel #4601. The new mesh-timeout forwarding exposes a reproducible CPU-offload failure during GRPO initialization. Once the upstream fix lands, this PR can proceed with an updated Automodel pin and regression coverage.
| ep_size=ep_size, | ||
| ), | ||
| world_size=world_size, | ||
| timeout_minutes=_distributed_timeout_minutes(config), |
There was a problem hiding this comment.
The mesh-timeout forwarding depends on fixing Automodel #4601. With CPU offload enabled, an explicit timeout currently creates NCCL-only mesh groups, causing prepare_refit_info() → DTensor.full_tensor() to fail with No backend type associated with device type cpu.
Reproduced with Qwen2.5-1.5B on two H100s: timeout 45 fails before training; timeout null completes one step.
Please hold this PR until the upstream fix lands, then update the Automodel pin and add a two-GPU regression test covering CPU offload with an explicit timeout.
|
Agreed, thanks for catching it — forwarding the timeout to the mesh exposes |
…timeout Spawn two ranks through the real setup_distributed with cpu_offload=True and distributed_timeout_minutes=45 (default group cuda:nccl,cpu:gloo) and check that the default group and every mesh axis keep both backends with the 45-minute timeout, that CPU and CUDA all-reduces work on every axis, and that a CPU-offloaded FSDP parameter sharded on device_mesh['dp'] comes back through full_tensor() - the prepare_refit_info() path of NVIDIA-NeMo/Automodel#4601. With the current Automodel pin the rank bodies fail with 'No backend type associated with device type cpu'; with NVIDIA-NeMo/Automodel#4602 applied the test passes. Until the pin includes that fix the test skips with that reason; the guard can go once the pin is bumped. Signed-off-by: linmuchuiyang <6169320+linmuchuiyang@users.noreply.github.com>
|
Automodel #4602 is merged ( Two-GPU regression test — added Automodel pin — I did not bump it in this PR. The pin would move from Automodel 0.6.1 to 0.8.0 (68 commits), and Automodel's own requirements moved with it: |
…PO recipe Apply the performance suggestions from @linmuchuiyang in #4246 to the 12-node V4.1 Flash DAPO recipe: - Generation: enforce_eager false with compilation_config.backend eager (CUDA graphs without Inductor) and max_num_seqs 32 instead of 8. - Training: the HybridEP MoE dispatcher instead of torch. HybridEP needs two DTensor policy options, both opt-in: - dtensor_cfg.hybridep_max_tokens_per_rank allocates Automodel's shared HybridEP buffer for the micro-batch token budget before the first forward. Automodel otherwise sizes it from the first dispatch and reallocates it when a larger micro-batch arrives, a mid-step collective that has failed across nodes with "Failed to create QP". - dtensor_cfg.distributed_timeout_minutes sets the NCCL timeout of the default process group and of the device-mesh groups (ported from the setup part of #4876). HybridEP's first-use JIT and in-step TileLang compiles can keep ranks waiting longer than the 10-minute default. Before colocated generation wakes up, the policy worker now also drops the per-dispatch tensors that flex MoE dispatch managers keep (ported from #4756); after activation-checkpointed backward they hold recompute graphs alive and pin allocator segments that empty_cache() cannot release. HybridEP requires all EP ranks in one NVLink domain, so the 12 nodes must come from one GB200 NVL72 rack (Slurm --segment=12 on Lyris). Verified on Lyris GB200, 12 nodes: - 30 steps (job 3594829): generation about 28 s/step vs 640-920 s in the recipe's torch-dispatcher reference run; steps 11-30 averaged 77 s policy_training and 59 s logprobs vs 133 s and 100 s. AIME 0.433/0.492/0.563 at steps 10/20/30 vs 0.450/0.529/0.579; generation KL 0.0004-0.0005. - Checkpoint save/resume (jobs 3611584 + 3611585): saves at steps 2/4/6/8 took 66-116 s, the resumed job continued at step 5, and KL stayed at 0.0004-0.0005. Co-authored-by: linmuchuiyang <6169320+linmuchuiyang@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: alexchiu <qiuzhaopeng@foxmail.com>
What does this PR do ?
Adds
policy.automodel_cfg.distributed_timeout_minutes(andvalue.automodel_cfg.…for PPO), the Automodel-path counterpart of themegatron_cfg.distributed_timeout_minutesknob that #4481 added for the Megatron workers.setup_distributedpassestimeout=datetime.timedelta(minutes=…)totorch.distributed.init_process_group, so the default group and every FSDP2 / expert-parallel sub-group derived from it use the configured collective timeout instead of torch's default (10 minutes for NCCL). Unset /nullkeeps the default; non-positive values are rejected with the same message pattern as #4481.Why: on the Automodel/DTensor path the first training step JIT-compiles kernels (HybridEP / DeepEP dispatch, TileLang attention) and loads large MoE checkpoints; on cold nodes those phases can keep ranks apart for more than 10 minutes, and the only way to survive it today is an environment-variable patch of
setup.py. The knob was needed to run the DeepSeek V4.1 Flash recipe (deepseek-v4.1-support) on H100 (NeMo RL #4246, data-point comment) and GLM-5.2 / DeepSeek-V4 Automodel fine-tuning on H100 and GB200.Changelog
nemo_rl/models/automodel/setup.py:_distributed_timeout_minutes()(validation) and_init_process_group()(backend + optional timeout);setup_distributedcalls it.nemo_rl/models/policy/__init__.py:AutomodelConfig.distributed_timeout_minutes: NotRequired[float | None].examples/configs/{dpo,grpo_math_1B,grpo_math_1B_megatron,ppo_math_1B,rm,sft,vlm_mpo}.yaml:distributed_timeout_minutes: nullwith a comment under everyautomodel_cfgblock (same files fix(megatron): make the NCCL collective timeout configurable #4481 annotated formegatron_cfg, plusrm.yaml).tests/unit/models/automodel/test_automodel_setup.py:TestDistributedTimeoutMinutes(forwarded with and without CPU offload backend, unset keeps the default, non-positive rejected).Issues
Related: #4481 (Megatron counterpart), #2229 / #2390 (exposing hard-coded infrastructure parameters), #4246.
Usage
Before your PR is "Ready for review"
tests/unit/models/automodel/test_automodel_setup.py::TestDistributedTimeoutMinutes). Locally the two helpers were exercised with the same four cases in a standalone harness (7 passed); the module-level test needsmain's full environment (our container is the olderdeepseek-v4.1-supportstack), so please run it in CI