ci(gpu): run the whole GPU test workflow at 4 GPUs - #218
Conversation
The suite was split across two widths: four jobs already pinned
TEST_MAX_GPUS=4, while training-smoke and generator-inference-smoke ran at
the default 8. Only ALL_NUM_GPUS = (0, 1, MAX_GPUS) exists per process, so
that split forced every multi-GPU test file to guard its own definition on
`if MAX_GPUS == 4` or `== 8` and made the 8-wide files uncollectable under a
4-GPU invocation.
Nothing in the two 8-wide jobs actually needs 8 ranks:
* edge_inference_smoke and nano_inference_smoke use
--parallelism-preset=throughput, which _build_context_parallelism derives
from world_size (cfgp=1, cp=1, dp_shard=world_size). The width is free.
* nano_training_smoke drives vision_sft_nano_5iter.toml, whose
data_parallel_shard_degree = -1 means "auto from WORLD_SIZE", and asserts
a loss trend rather than a golden, so the batch-size change that comes
with a narrower run does not invalidate it.
So the hardcoded 8s become MAX_GPUS and the collection guards accept either
width, matching what nano_inference_smoke_test.py already did. The two jobs
gain TEST_MAX_GPUS=4 and their --num-gpus flags follow; the env var and the
flag have to agree or collection either errors on an out-of-range choice or
deselects everything.
unittest passes no --num-gpus and only carries gpus(0)/gpus(1) markers, so
it is unaffected either way; it is pinned too so the workflow has a single
ALL_NUM_GPUS and a gpus(8) marker added later fails collection instead of
reaching a runner that cannot host it.
What this gives up is coverage of the 8-wide FSDP/CP path itself -- a bug
that only appears at a larger world size is no longer caught here. The
ModelOpt FP8 cases are handled separately in the next commit; left alone
they would silently skip via their MAX_GPUS < 8 guard.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
_FP8_LAYOUTS pinned dp_shard_size=8 and cp_size=8 literally, so the two ModelOpt FP8 cases carried a `skipif(MAX_GPUS < 8)`: ParallelDims rejects a layout whose degree product is not WORLD_SIZE, and there was no 4-wide equivalent to fall back to. There is one -- the degrees are the only thing that was 8. Following MAX_GPUS keeps `sharded` a pure-FSDP layout and `replicated` a pure-context-parallel one, which is the distinction the two cases exist to draw, so the skipif goes away and the previous commit does not silently drop FP8 coverage. This is a real change to the configuration under test, not just a count: `sharded` now exercises the TorchAO static-FP8 FSDP2 shim at a 4-way all-gather rather than 8-way, and cp_size=4 is a different collective schedule than cp_size=8 (both are within MAX_CP_SIZE=32). Separated from the width switch so it can be reverted on its own if the narrower layouts turn out not to hold. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Execution resultsFilling in the placeholder from the PR body. Run on the real CI runner (H200), not just the GB200 box I used while developing.
|
| Job | Result |
|---|---|
| pre-commit (x2) | pass |
| training-smoke | pass, 11m31s |
| generator-training-regression | pass, 11m3s |
| generator-inference-smoke | pass, 10m42s |
| distilled-inference-smoke | pass, 2m6s |
| reasoner-inference-smoke | pass, 3m13s |
| reasoner-training-regression | pass, 2m19s |
| unittest | still running at time of writing |
Offline checks (4xGB200, aarch64 — different arch from CI)
- Collection under
TEST_MAX_GPUS=4 --num-gpus=4 --levels=2across all seven GPU test files: exactly 15 tests, including the two FP8 cases that previously skipped. TEST_MAX_GPUS=8 --num-gpus=8still parses with no marker errors (all deselected on a 4-GPU host, as designed).- The
unittestjob's exact invocation collects identically withTEST_MAX_GPUS=4and unset: 1230/1233 collected, 3 deselected, same 3 pre-existing container-dependency collection errors either way. Confirms that pinning the width there is a no-op. ruff checkclean on the three touched files. Theruff formatdeviations in those files are pre-existing and sit in code this PR does not touch; pre-commit configures no ruff hook, so they are not gated.
Follow-up, not for this PR
_detect_arch in launch_regression_test.py maps only GB200/H100/H200 and pytest.skips anything else. Moving the suite to a different GPU arch would make both regression jobs skip silently and still report green. Not a problem for a same-model-type capacity change, but worth turning into a hard failure separately.
What
Drops the GPU workflow from a mixed 8-and-4 layout to 4 GPUs everywhere.
Four of the seven GPU jobs already pinned
TEST_MAX_GPUS: "4". Onlytraining-smokeandgenerator-inference-smokeran at the default 8. BecauseALL_NUM_GPUS = (0, 1, MAX_GPUS)admits exactly one multi-GPU width perprocess, that split forced each multi-GPU test file to guard its own
definitions behind
if MAX_GPUS == 4/== 8, and made the 8-wide filesuncollectable under a 4-GPU invocation.
Why this is safe
Neither 8-wide job needs 8 ranks:
edge_inference_smoke/nano_inference_smokerun with--parallelism-preset=throughput, and_build_context_parallelismderivesthat layout from
world_size(cfgp=1, cp=1, dp_shard=world_size). The widthis free.
nano_training_smokedrivesvision_sft_nano_5iter.toml, whosedata_parallel_shard_degree = -1means "auto from WORLD_SIZE", and asserts aloss trend rather than a golden — so the batch-size change a narrower run
brings does not invalidate it.
The hardcoded 8s become
MAX_GPUS; the collection guards accept either width,matching what
nano_inference_smoke_test.pyalready did.--num-gpusandTEST_MAX_GPUSmust agree or collection either errors on an out-of-rangechoice or deselects everything, so both move together.
unittestpasses no--num-gpusand carries onlygpus(0)/gpus(1)markers,so it is unaffected either way. It is pinned anyway so the workflow has a single
ALL_NUM_GPUSand agpus(8)marker added later fails collection instead ofreaching a runner that cannot host it.
The FP8 commit, separately
_FP8_LAYOUTSpinneddp_shard_size=8/cp_size=8literally, which is whythe two ModelOpt FP8 cases carried
skipif(MAX_GPUS < 8). Left alone they wouldhave silently skipped. The second commit makes both degrees follow
MAX_GPUS,keeping
shardeda pure-FSDP layout andreplicateda pure-CP one — thedistinction the two cases exist to draw. This genuinely changes the
configuration under test (a 4-way rather than 8-way all-gather through the
TorchAO static-FP8 FSDP2 shim), so it is split out and can be reverted alone.
Verification
Run on 4×GB200 (
TEST_MAX_GPUS=4 --num-gpus=4 --levels=2), not the H200 CI arch:files, including
test_nano_fp8_inference[sharded]and[replicated], whichpreviously skipped.
TEST_MAX_GPUS=8 --num-gpus=8still parses with no marker errors (alldeselected on a 4-GPU host, as designed).
ruff checkclean on the three touched files.ruff formatdeviations inthose files are pre-existing and not gated — pre-commit configures no ruff hook.
What this gives up
Coverage of the 8-wide FSDP/CP path itself: a bug that only appears at a larger
world size is no longer caught here.
🤖 Generated with Claude Code