slurm: batch the queries and parallelize the writes in the GPU power/clock helpers - #1393
slurm: batch the queries and parallelize the writes in the GPU power/clock helpers#1393100milliongold wants to merge 2 commits into
Conversation
…clock helpers set_gpu_power_levels.sh and set_gpu_clocks.sh called nvidia-smi once per GPU to read the target value and once more to apply it, all serially. Both "nvidia-smi -pl" and "nvidia-smi -ac" take roughly a second per GPU, so on an 8-GPU node the two helpers together add about 8 s to the prolog of every job that 50-exclusive-gpu runs for. srun reports this as: srun: Prolog hung on node <node> Read the values for all GPUs in a single --query-gpu call, then apply them in parallel and collect each child's exit status so a failure on any GPU still fails the script. Behaviour is otherwise unchanged: the same values are written to the same GPUs. The "default" branch of set_gpu_clocks.sh already operated on all GPUs at once and is untouched. Observed on DGX OS 7.5.0 (8x B300), Slurm 26.05.1: prolog took 6-8 s per job while 50-exclusive-gpu was running. Signed-off-by: Jea-Eok-Kim <je.kim@xiilab.com>
dholt
left a comment
There was a problem hiding this comment.
The batched queries at set_gpu_power_levels.sh:21 and set_gpu_clocks.sh:13-14 run in process substitutions, whose failures are invisible to readarray and set -e. If a query fails with empty output, the loop is empty and the helper exits 0; partial output can update only some GPUs and also return success. Capture each query through a construct whose status can be checked, validate that all required per-GPU rows are present and aligned, and only then launch writes. Please cover empty, partial, and nonzero query results as required by the changed-path evidence.
Automated triage review (agent-generated on the maintainer's behalf; a human maintainer decides merges).
`readarray -t limits < <(nvidia-smi ...)` hides the query's exit status from
both readarray and `set -e`. The helpers therefore reported success in every
failure mode: an empty result made the write loop run zero times, a truncated
result configured only some of the GPUs, and a nonzero exit was not seen at all.
The query result now goes through a file so its status can be checked, the index
is selected alongside the values so a write targets the GPU nvidia-smi reported
instead of an array subscript, and every row is parsed and range-checked before
the first write is launched. The row count is compared against `nvidia-smi -L`,
so a partial result is rejected rather than silently applied.
set_gpu_clocks.sh additionally selected clocks.max.mem and clocks.max.sm in two
separate queries. If the two returned different row counts, `${maxMEM[$i]}` was
empty for the trailing GPUs and produced an `-ac ,1980` argument. Both values
now come from the same query, so they cannot drift out of alignment.
Verified on a DGX B300 (Ubuntu 24.04, bash 5.2) with an nvidia-smi stub that
honours --query-gpu and records writes instead of performing them. Identical
results for both helpers:
case before after
---------- ---------------------------- ---------------------
ok rc=0 8 writes rc=0 8 writes
empty rc=0 0 writes rc=1 0 writes
partial rc=0 3 writes (of 8 GPUs) rc=1 0 writes
fail rc=0 0 writes rc=1 0 writes
nonnumeric rc=0 8 writes ("N/A" passed) rc=1 0 writes
One limitation of the stub is worth stating: it records writes rather than
performing them, so the `nonnumeric` row shows 8 writes for the old code. On
real hardware `nvidia-smi -pl N/A` fails and `wait` would surface rc=1 — but
only after eight bad invocations. The new code rejects the row before the first
one.
The `ok` case is unchanged, so the parallel-write speedup this branch adds is
preserved.
|
Thanks — the process-substitution point is correct, and the failure modes were worse than "invisible": all four of them returned success. What changed (commit
Coverage of the cases you asked for, verified on a DGX B300 (Ubuntu 24.04, bash 5.2) with an
One limitation of the stub is worth stating plainly: it records writes rather than performing them, so the The |
Problem
set_gpu_power_levels.shandset_gpu_clocks.shcallnvidia-smionce per GPU to read the target value and once more to apply it, all serially. Bothnvidia-smi -plandnvidia-smi -actake roughly a second per GPU, so on an 8-GPU node the two helpers together add about 8 s to the prolog of every job for which50-exclusive-gpuruns.srunsurfaces it as:Observed on DGX OS 7.5.0 (8× B300), Slurm 26.05.1.
Fix
--query-gpucall (the per-GPU-iloop was only needed because the value was read one at a time).The same values are written to the same GPUs; only the number of
nvidia-smiinvocations and their concurrency change. Thedefaultbranch ofset_gpu_clocks.shalready operated on all GPUs at once and is untouched.Verification
Not yet timed with the patched scripts on hardware — the system where this was found has
50-exclusive-gpuremoved fromprolog.d(an 8-GPU node shared between jobs should not have every job reset limits and clocks on all GPUs). The serial cost is reproducible there: prolog took 6–8 s per job while the script was in place. Marked as draft for that reason; happy to run a timed before/after if that would help.Related
The reason every job ran
50-exclusive-gpuin the first place is a separate defect in the exclusive-job detection, addressed in #1391.