[Multi-GPU] Refactors multi-gpu logic into module, fix ghost process on keyboard interrup - #6975
Conversation
Greptile SummaryThe PR moves multi-GPU launcher construction into
Confidence Score: 4/5The fallback shutdown behavior should be fixed before merging because interrupted distributed runs can still leave worker processes and GPU allocations behind on platforms without process groups. POSIX shutdown is substantially improved, but the newly retained fallback ends only the direct launcher process and provides no mechanism to terminate or reap its distributed worker descendants. Files Needing Attention: source/isaaclab_rl/isaaclab_rl/entrypoints/multigpu.py Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
CLI[train_multigpu CLI] --> Check{os.killpg available?}
Check -->|Yes| Group[Start launcher in new session]
Group --> Forward[Forward signal to process group]
Forward --> Escalate[Escalate and reap descendants]
Check -->|No| Fallback[Start direct child]
Fallback --> Terminate[Terminate direct launcher only]
Terminate --> Orphans[Worker descendants may survive]
Reviews (1): Last reviewed commit: "Stop multi-GPU Ctrl-C from leaving worke..." | Re-trigger Greptile |
| proc.terminate() | ||
|
|
There was a problem hiding this comment.
Fallback leaves workers running
When multi-GPU training is interrupted on a platform without os.killpg, this handler terminates only the direct torchrun or skrl launcher and performs no descendant cleanup or escalation, causing worker processes to continue running and retain GPU resources after the CLI exits.
There was a problem hiding this comment.
Isaac Lab Review Bot
The launcher refactor centralizes multi-GPU command construction and process supervision in isaaclab_rl.entrypoints, but the single-process training shim no longer sets Warp’s enable_backward flag before importing the entrypoints package, weakening the import-order guarantee the previous implementation explicitly preserved.
- Design and architecture: Moving launcher logic into the package and retaining the script as a compatibility shim provides a cleaner shared entrypoint. The new worker module is a reasonable per-rank target, but importing it through
isaaclab_rl.entrypointsexecutes the package initializer before the worker can configure Warp. - API: The existing multi-GPU CLI behavior is retained through
run_train_multigpu_cli, which is exported from bothisaaclab_rl.entrypointsand the package-level lazy API. No incompatible CLI change is evident in the supplied patch. - Implementation: The process-group supervision, signal escalation, command building, and shutdown regression tests directly address orphaned workers. However,
train.pynow importsisaaclab_rl.entrypoints.train_workerbefore the worker setswp.config.enable_backward = False; Python executesisaaclab_rl.entrypoints.__init__first, so the flag should remain configured intrain.pybefore that import to preserve the stated ordering guarantee.
Minor fixes needed. Posted 1 actionable finding inline.
Automated review; human maintainers own approval decisions.
|
|
||
| # Importing the worker sets Warp's ``enable_backward`` before any Warp kernels are defined, which | ||
| # has to happen before the training stack is imported. | ||
| from isaaclab_rl.entrypoints.train_worker import main |
There was a problem hiding this comment.
🟡 Warning · Implementation — Warp flag set after entrypoints package import
from isaaclab_rl.entrypoints.train_worker import main first executes isaaclab_rl/entrypoints/__init__.py (importing api, dispatch, multigpu); only afterwards does train_worker's body run wp.config.enable_backward = False. The removed code deliberately set the flag before that exact import, so the ordering guarantee the new comment claims no longer holds on the single-process path. Set the flag in train.py before importing the worker.
The launcher lived entirely in scripts/reinforcement_learning, which left it outside the package that already owns the train and play entry points and made its logic unavailable to anything but that one script. Move it to isaaclab_rl.entrypoints.multigpu, exposed as run_train_multigpu_cli, and reduce the script to a shim like train.py and play.py. The module doubles as the per-rank trainer: the launcher points torchrun at this file, so its __main__ block is what each rank runs. That keeps the worker resolvable from the package rather than from a path relative to the script, which matters for wheel installs. The list of distributed-capable backends moves to api.MULTI_GPU_BACKENDS so it sits next to BackendName instead of being restated in the launcher, and the skrl JAX exclusion list is now derived from the torchrun options rather than spelled out a second time.
The launcher ran torchrun inside its own process group, so a terminal Ctrl-C was delivered to torchrun and every worker at the same moment the launcher forwarded a signal of its own. That second signal landed inside torchelastic's shutdown handler, which then aborted partway through reaping the workers it had spawned, leaving them alive and holding GPU memory. Start the worker tree in a new session so the terminal signals only the launcher, forward one signal from that single place, and escalate to SIGTERM and then SIGKILL. A worker wedged in a native CUDA, NCCL, or renderer call never returns to Python to observe a signal at all, so the launcher also sweeps the process group before it returns.
4e6fad5 to
8f18c2c
Compare
Description
Refactors multi-gpu logic from train_multigpu into isaaclab_rl
Checklist
pre-commitchecks with./isaaclab.sh --formatsource/<pkg>/changelog.d/for every touched package (do not editCHANGELOG.rstor bumpextension.toml— CI handles that)CONTRIBUTORS.mdor my name already exists there