Repository navigation
Conversation
CODEOWNERS review mapCurrent for commit ⏳ @coreyjadams — 2 file(s)
⏳ @negin513 — 2 file(s)
No CODEOWNER
Comment |
Greptile SummaryFixes direct DistributedManager.setup() use on CPU-only hosts and makes cleanup safe when no process group exists.
Important Files Changed
Reviews (1): Last reviewed commit: "Fix DistributedManager initialization on..." | Re-trigger Greptile |
| n_devices = torch.cuda.device_count() | ||
| manager._local_rank = rank % n_devices if n_devices > 0 else 0 |
There was a problem hiding this comment.
CPU fallback misses environment initialization
If a CPU-only process has RANK and WORLD_SIZE set without LOCAL_RANK, initialize_env() evaluates rank % torch.cuda.device_count() before reaching this fallback, causing initialization to fail with ZeroDivisionError before the process group is created.
Knowledge Base Used: Distributed Execution Primitives
There was a problem hiding this comment.
Good catch — confirmed: with RANK/WORLD_SIZE set and no LOCAL_RANK on a CPU-only host, initialize_env() divided by the accelerator count before setup() ran. Fixed in c0d5b25 by leaving local_rank as None there and letting setup() derive it (rank modulo the accelerator count, or 0 on CPU-only hosts), with a regression test for the environment-variable initialization path.
DistributedManager.setup() failed on hosts without accelerators: - it passed the CPU torch.device as the device_id argument of torch.distributed.init_process_group, which recent PyTorch versions reject with 'ValueError: init_process_group device_id parameter must be an accelerator with an index'; - with local_rank=None it derived the local rank as rank % torch.cuda.device_count(), a division by zero when no accelerator is present. Bind a device to the process group only on CUDA, and default local_rank to 0 on CPU-only hosts. Also guard cleanup() on the process group actually existing, so a failed initialization no longer cascades into 'AssertionError: Process group cannot be None' at exit. Verified with a two-process gloo run launched by torchrun on a CPU-only machine (initialize, broadcast, barrier, cleanup), and add a regression test that forces the CPU code path via monkeypatched torch.cuda.is_available / device_count. Signed-off-by: Piyoosh Jaysaval <piyoosh.jaysaval@pnnl.gov>
initialize_env() computed rank % torch.cuda.device_count() before calling setup(), so CPU-only hosts with RANK and WORLD_SIZE set but no LOCAL_RANK still failed with ZeroDivisionError. Leave local_rank as None in that case and let setup() derive it (rank modulo the accelerator count, or 0 on CPU-only hosts), and add a regression test for the environment-variable initialization path. Signed-off-by: Piyoosh Jaysaval <piyoosh.jaysaval@pnnl.gov>
f0387e1 to
c0d5b25
Compare
peterdsharpe
left a comment
There was a problem hiding this comment.
Looks good to me! Checked this works on my end too. Thanks!
|
/ok to test c0d5b25 |
|
Thanks again for the review @peterdsharpe. This has been approved with CI green for a couple of weeks now but appears to still be waiting on a CODEOWNERS review to merge. Is there anything needed from my side? I've held off rebasing onto main to keep the approval and CI state intact, but happy to update the branch if that's preferred. |
Resolve the conflict with NVIDIA#1968 (process-group timeout): keep the CPU-safe device_id and pass the new timeout argument. Signed-off-by: Piyoosh Jaysaval <piyoosh.jaysaval@pnnl.gov>
|
@coreyjadams @negin513, this PR needs a review from one of you as CODEOWNERS of physicsnemo/distributed/. It was approved by @peterdsharpe on Aug 19 with CI green and has been waiting since. I have just merged current main into the branch to resolve the conflict with #1968: the CPU-safe device_id is kept and the new timeout argument is passed through unchanged, so the functional diff is the same three hunks as before (CPU device_id, zero-safe local_rank, guarded cleanup()), plus the regression tests. Happy to squash or adjust anything you would like. |
|
/ok to test 93604eb |
Description
DistributedManager.setup()failed on hosts without accelerators (e.g. laptop-scale gloo runs, CPU nodes):torch.deviceas thedevice_idargument oftorch.distributed.init_process_group, which recent PyTorch versions reject withValueError: init_process_group device_id parameter must be an accelerator with an index;local_rank=None(the SLURM/OpenMPI initialization paths) it derived the local rank asrank % torch.cuda.device_count(), a division by zero when no accelerator is present.This PR binds a device to the process group only on CUDA (
device_id=Noneotherwise, the documented default), defaultslocal_rankto 0 on CPU-only hosts, and additionally guardscleanup()on the process group actually existing so a failed initialization no longer cascades intoAssertionError: Process group cannot be Nonefrom theatexithandler, obscuring the original error.Verified with a two-process gloo run launched by
torchrunon a CPU-only machine (initialize → broadcast → barrier → cleanup). Adds a regression test that forces the CPU code path via monkeypatchedtorch.cuda.is_available/device_count, so it exercises the fix even on GPU CI (the existing distributed-manager tests are skipped on CPU by an autouse fixture, which is how this path went unnoticed). Also adds docstrings to two pre-existing undocumented methods inmanager.pyflagged by theinterrogatepre-commit hook.closes #1936
Checklist
Dependencies
None.