Repository navigation
Allocate CI GPUs per task instead of per job - #571
Conversation
The runner has 4 job slots for 8 GPUs, and a job waiting for GPUs still holds its slot. With one fixed GPU request per job, a 2-GPU job plus three waiting 8-GPU jobs fill every slot and leave 6 GPUs idle while the small jobs that could use them sit in GitHub's queue. Over 78 recent jobs, 8-GPU jobs waited 698 s on average for 213 s of testing, and GPUs were 58% utilized while CI was busy. gpu_task_queue.py runs a job's tasks concurrently and takes GPUs per task from the same bitmap and lock as gpu_allocator.sh: largest task first, smaller ones backfill free GPUs, and GPUs go back the moment a task ends. A task that has waited 5 minutes posts a reservation that stops new work everywhere (including the bash allocator) until it fits, so 8-GPU tasks are not starved; a larger request takes over a smaller one's reservation. Each task runs in a private copy of the checkout since editable/pip installs write into the source tree. iris-tests.yml now has one job per test directory (all rank counts) for git installs, and one job each for editable, pip, and new examples. The bash allocator polls every 10 s instead of 60 s, and release_gpus.sh returns GPUs a killed queue left behind. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
d294a50 to
3eb68a4
Compare
There was a problem hiding this comment.
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Copilot review overview
Review effort: Lite
Findings: 2
Open (5)
exit 1insideacquire_gpus()will terminate the entire script (and likely the workflow step)… · New The held-mask file path defaults to a fixed filename (.../iris_gpu_queue_held). IfRUNNER_TEMP… · New SinceHELD_FILEis used as crash-recovery state for releasing GPUs after an abrupt termination,… · New Copying the entire workspace (including.git) per task can become a noticeable IO/disk bottleneck… · New Creating a directory withmkdtemp()and immediately deleting it to satisfycopytree()is… · New
What changed in this PR
This PR restructures the GPU CI workflow to reduce runner-slot blockage by allocating GPUs per task (within a job) rather than per job, with an anti-starvation reservation mechanism shared between the new Python queue and the existing bash allocator.
Changes:
- Replace matrix-per-rank workflow jobs with bundled per-directory (and per-install-type) jobs that run multiple rank tasks concurrently via
gpu_task_queue.py. - Introduce a Python task queue/allocator that shares bitmap + flock protocol with
gpu_allocator.sh, plus reservation support to prevent starvation of large (e.g., 8-GPU) tasks. - Update the bash allocator to poll more frequently and to respect node reservations; update reset logic to clear reservations; enhance GPU release to recover allocations after abrupt queue termination.
| File | Description |
|---|---|
| .github/workflows/iris-tests.yml | Bundles multiple rank runs per directory/install type and invokes gpu_task_queue.py instead of per-job GPU acquisition. |
| .github/workflows/gpu-reset.yml | Resets allocator reservation state in addition to the bitmap. |
| .github/scripts/release_gpus.sh | Adds fallback release path using a held-mask file written by gpu_task_queue.py. |
| .github/scripts/gpu_task_queue.py | New per-task GPU allocator/runner with reservation-based anti-starvation and per-task workspaces/logging. |
| .github/scripts/gpu_allocator.sh | Reduces polling interval and blocks new allocations while a reservation is active. |
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
mkdtemp() followed by rmtree() just to give copytree() a missing target was roundabout. Copy into <tmpdir>/src and keep the log beside it in <tmpdir>/task.log, so a single rmtree cleans up both. dirs_exist_ok would also work but needs Python 3.8 on the runner host. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
|
@nirvedhmeshram noticed not all the GPU workflows are using the new queue. Only Also, can we do an eval of current vs before? |
|
@mawad-amd thanks. Both points are addressed in #578, which moves perf, external validation, nightly and the CCL benchmark onto the queue. Before/after #571. The after-window is only ~1.5 days (9 Iris Tests runs, 49 perf jobs), so read these as indicative.
#578 also fixes the Triton external validation test, which never passed its GPUs to the container and could see all 8. I'll re-run this comparison once there's a week of data with #578 in. 🤖 Generated with Claude Code |



Problem
The runner has 4 job slots for 8 GPUs, and a job waiting for GPUs still holds its slot. With one fixed GPU request per job, a common state is one 2-GPU job running and three 8-GPU jobs spinning in "Acquire GPUs". That leaves 6 GPUs idle, while the 1/2/4-GPU jobs that could use them wait in GitHub's queue, which we can't reorder.
Measured over 78 jobs from the 20 most recent completed GPU workflow runs:
GPUs were 58% utilized while CI was busy, and 16.6 of 40.4 GPU-hours sat idle while some job was spinning on acquire. Full
Iris Testsruns took 89 and 115 min, against roughly 60 and 79 min of GPU work if perfectly packed.Change
gpu_task_queue.py: a job gets a list of<test_dir>:<ranks>tasks, runs them concurrently, and takes GPUs per task. It uses the same bitmap andflockasgpu_allocator.sh, so it coexists with the perf and external-validation jobs that still use the bash allocator./tmp/iris_gpu_state.reserve). New work then stops everywhere, including in the bash allocator, until the reserved task fits. A larger request takes over a smaller one's reservation; the owner refreshes it, and it expires after 10 min if the owner dies..git, which setuptools-scm needs. It's about 21 MB.Release GPUsalways()step returns its GPUs using a held-mask file.iris-tests.ymlrestructuring:gpu_allocator.sh:gpu-reset.ymlnow also clears the reservation file.Trade-offs
Testing
Tested locally with stub tasks that sleep instead of using GPUs:
release_gpus.shfallbackNot yet verified on the real runners. This PR's own CI run is the first real test.
🤖 Generated with Claude Code