fix: honor cuda_devices in Singularity backend and support multiple GPUs in Docker - #176
Conversation
…PUs in Docker Singularity's --nv flag exposes all host GPUs and previously ignored the cuda_devices parameter. The Singularity backend now sets SINGULARITYENV_CUDA_VISIBLE_DEVICES around the container run so only the requested host GPU IDs are visible inside the container, restoring any previous value afterwards. The Docker backend now accepts a comma-separated list of device IDs (e.g. "0,1"), splitting on commas and stripping whitespace. Closes #164
There was a problem hiding this comment.
Pull request overview
This PR fixes GPU device selection inconsistencies across container backends in the BraTS orchestrator, ensuring cuda_devices is actually enforced in Singularity and correctly supports multi-GPU selection in Docker.
Changes:
- Singularity now restricts visible GPUs by setting
SINGULARITYENV_CUDA_VISIBLE_DEVICESduring the container run (and restores any prior value). - Docker now supports comma-separated multi-GPU selection by splitting
cuda_devicesinto multiple device IDs. - Documentation and architecture notes were updated to clarify that
cuda_devicesrefers to host GPU IDs and how it interacts with host-level CUDA environment variables.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
brats/core/singularity.py |
Adds a context-managed environment override so Singularity honors cuda_devices while running with --nv. |
brats/core/docker.py |
Updates Docker device request construction to support multiple GPU IDs via comma-splitting. |
tests/core/test_singularity.py |
Adds tests validating Singularity CUDA env override/restore behavior and CPU-path behavior. |
tests/core/test_docker.py |
Adds a test ensuring Docker splits multi-GPU cuda_devices correctly (including whitespace tolerance). |
docs/guides/backends.md |
Documents multi-GPU selection and clarifies Singularity GPU restriction behavior. |
AGENTS.md |
Updates backend dispatch documentation to reflect cuda_devices being honored by both backends. |
Suppressed comments (1)
tests/core/test_singularity.py:417
- This test asserts the CUDA env var is absent after the run, but it may already be set in the caller environment. To verify the CPU path truly doesn't touch the env, explicitly set a sentinel value and assert it remains unchanged (with cleanup).
self.assertIsNone(captured_env["value"])
options = mock_client.run.call_args.kwargs["options"]
self.assertNotIn("--nv", options)
self.assertNotIn("SINGULARITYENV_CUDA_VISIBLE_DEVICES", os.environ)
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
- Add shared normalize_cuda_devices helper (split on commas, strip whitespace, drop empty entries, raise ValueError if nothing remains) and use it in both the Docker and Singularity backends so input like "0," or " 0 , 1 " is handled consistently - Make the Singularity CUDA env tests hermetic: the GPU test no longer assumes SINGULARITYENV_CUDA_VISIBLE_DEVICES is unset, and the CPU test uses a sentinel value to prove the environment is not touched - Document that the Singularity GPU-selection env override is process-global and not thread-safe (runs should be sequential)
There was a problem hiding this comment.
🟡 Changes recommended
Process-global environment mutation can select incorrect GPUs during concurrent Singularity runs.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 8/8 changed files
- Comments generated: 2
- Review effort level: Balanced
Use patch.dict(os.environ) instead of a manual try/finally pop so any pre-existing SINGULARITYENV_CUDA_VISIBLE_DEVICES value on the runner is restored rather than deleted after the test.
neuronflow
left a comment
There was a problem hiding this comment.
lgtm
(but no time for an in-depth review :( )
|
in general we should find an agreement with singularity vs. apptainer #153 |
Summary
Fixes #164.
cuda_deviceswas silently ignored — the--nvflag exposes all host GPUs. The Singularity backend now setsSINGULARITYENV_CUDA_VISIBLE_DEVICESaround the container run (Singularity passes it into the container asCUDA_VISIBLE_DEVICES), so only the requested host GPUs are visible. Any previously set value is restored after the run, and a warning is logged if it was already set.cuda_devicesnow accepts a comma-separated list of device IDs (e.g."0,1"); entries are split on commas and whitespace is stripped.cuda_devicesrefers to host GPU IDs and takes precedence over a host-levelCUDA_VISIBLE_DEVICES(e.g. set by SLURM) inside the container; updated AGENTS.md anddocs/guides/backends.mdaccordingly.Testing
uv run pytest— 93 passed (tests mock container execution; no Docker daemon/GPU required)uv run ruff check .anduv run ruff format --check .— clean