Fix three DDP correctness issues: PMI guardrails, data padding, unuse… - #2694
Open
edopica wants to merge 4 commits into
Open
Fix three DDP correctness issues: PMI guardrails, data padding, unuse…#2694edopica wants to merge 4 commits into
edopica wants to merge 4 commits into
Conversation
| l, grad = Zygote.withgradient(loss, model) | ||
|
|
||
| # Resolve nothing gradients to avoid deadlocks | ||
| grad_resolved = DistributedUtils.resolve_unused_parameters!!(backend, grad[1], model) |
Member
There was a problem hiding this comment.
Users shouldn't do this manually.
We should call resolve_unused_parameters!! internally, as part of Flux.withgradient or Optimisers.update. This could be part of a future PR, but for the time being I would avoid adding anything to the guide.
Comment on lines
+7
to
+19
| backend_string = ARGS[1] | ||
|
|
||
| if backend_string == "mpi" | ||
| import MPI | ||
| const backend_type = MPIBackend | ||
| elseif backend_string == "nccl" | ||
| import MPI, NCCL, CUDA | ||
| const backend_type = NCCLBackend | ||
| else | ||
| error("unsupported backend: $backend_string") | ||
| end | ||
|
|
||
| const dev = Flux.cpu |
Member
There was a problem hiding this comment.
this boilerplate code could be added to
https://github.com/FluxML/Flux.jl/blob/master/test/test_module.jl
instead of being part of each test file
| using Optimisers | ||
|
|
||
|
|
||
| backend_string = ARGS[1] |
Member
There was a problem hiding this comment.
instead of reading ARGS this should be set by ENV variables in
https://github.com/FluxML/Flux.jl/blob/master/test/runtests.jl
as anything else
Member
|
before merging this PR, CI should be activated for the distributed tests, currently it is off |
edopica
force-pushed
the
ddp/upstream-pr
branch
from
August 17, 2026 11:30
df95f60 to
504e816
Compare
…d parameters - Add PMIx/PMI2 mismatch detection in MPI backend initialization checks before MPI.Init(), with force kwarg bypass. Produces a clear error instead of a hard abort. - Implement data padding in DistributedDataContainer to ensure even distribution across workers, preventing load imbalance. - Add resolve_unused_parameters!! to replace nothing gradients with zero-filled arrays, preventing allreduce deadlocks in conditional-computation models. - Add missing imports in reduce_distributedtest.jl for CuArray and ROCArray, making it runnable under NCCL. - Rename orphaned distributed tests (common.jl, optimizer.jl, synchronized.jl, data.jl) to _distributedtest.jl suffix so they are discovered by the test runner. - Convert all distributed test files to single-arg ARGS pattern consistent with how the test runner launches them. - Add unused_parameters_distributedtest.jl test suite. - Add documentation in docs/src/guide/gpu.md for resolve_unused_parameters!!. - Add a new comprehensive distributed training guide in docs/src/guide/distributed.md. - Enable distributed MPI tests to run on Linux CI via test/runtests.jl.
- Flux's DistributedUtils.initialize already handles device isolation automatically - Explicit device assignment can conflict with NCCL backend initialization
edopica
force-pushed
the
ddp/upstream-pr
branch
from
August 17, 2026 11:34
504e816 to
bc40df2
Compare
…uite default) - test/runtests.jl: revert FLUX_TEST_DISTRIBUTED_MPI default to "false" (exactly upstream master); main suite no longer runs ext_distributed inside ParallelTestRunner, which passes no ARGS (BoundsError in backend_string = ARGS[1]) - .github/workflows/distributed_ci.yml: dedicated 2-rank fast + 4-rank edge MPI jobs, system OpenMPI + MPIPreferences, launcher invoked directly with --project=test so MPI resolves (root-project weakdep is NOT loadable via using; verified empirically) - test/ext_distributed/runtests.jl: watchdog timeout (120 s default, FLUX_TEST_DISTRIBUTED_TIMEOUT) + real exit-code propagation (proc.exitcode == 0) replacing the silent Test.@test true - test/Project.toml: MPI as explicit test dependency
The PMIx/OMPI env checks assumed MPICH_jll: with system OpenMPI (PMIx), PMIX_RANK is the correct protocol and the guardrail aborted every OpenMPI launch (CI MPI jobs failed). Guardrail now checks MPI.MPI_LIBRARY and skips PMIx/PMI2 mismatch errors and the Slurm PMI2 warning for OpenMPI-based libraries; messages name the loaded library. Verified: OpenMPI_jll 2/4-rank launcher runs pass, MPICH_jll path unchanged, MPICH + PMIX_RANK still errors.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fix three DDP deadlock/crash scenarios in DistributedUtils
This PR addresses three independent correctness issues in Flux's distributed
training path (MPI backend), originally introduced in the distributed data parallel PR (#2464). Each bug causes either a deadlock or a hard crash
when running DDP with non‑trivial models or launch configurations.
Additionally, this PR enables MPI distributed tests to run automatically on Linux CI runners.
1. PMI version mismatch guardrails
Problem: Julia's default
MPICH_jllspeaks PMI2. When launched under aPMIx launcher (e.g.
srunwithout--mpi=pmi2) or under OpenMPI'smpirun,MPI.Init()aborts with an uninformative message, making diagnosis difficult.Even inside a Slurm allocation with
mpiexecjl, the missing PMI2 variablesproduce a confusing hang.
Fix: Before calling
MPI.Init(), check for environment markers:PMIX_RANK→ error with PMIx mismatch messageOMPI_COMM_WORLD_RANK→ error with OpenMPI mismatch messageSLURM_JOB_IDbut noPMI2_*→ warn about missing--mpi=pmi2A
force=truekeyword allows advanced users to bypass the check (e.g. whenusing a custom MPI build). The logic is entirely inside
FluxMPIExtandruns before any MPI symbols are touched.
2. Padding in DistributedDataContainer to prevent allreduce deadlocks
Problem: When
n_samples % n_workers ≠ 0,DistributedDataContainerassigned fewer batches to the last rank. That rank would exit the training
loop early while the other ranks blocked inside an
allreducecall, causing apermanent deadlock.
Fix: Pad the index sequence so every rank receives the same number of
elements. Surplus slots are filled by wrapping around from the start
(
[1, 2, …, n_samples, 1, 2, …]). The data‑loading logic is unchanged;only the index array passed to
DistributedDataContaineris padded.3.
resolve_unused_parameters!!for conditional computation graphsProblem: A model with conditional branches (e.g.
if/elseselectingdifferent layers) can produce
nothinggradients on ranks that did not executea particular parameter. When
DistributedOptimizerlater callsallreduceover all gradients, the mismatched set of buffers deadlocks — different ranks
participate in different collectives.
Fix: New public function
resolve_unused_parameters!!(backend, gs, model)replaces every
nothinggradient with a zero‑filled array matching theparameter's shape, element type, and device. It walks nested structures
(NamedTuples, etc.) and works with arbitrary tree depths thanks to Functors'
fmap.Tests added / updated
test/ext_distributed/data_distributedtest.jldata.jlto run with the test suite. 3 test groups: evenly-divisible partition, non-divisible (padded) partition with correct padded sum, and the original 10‑element case with expected padded sum.test/ext_distributed/unused_parameters_distributedtest.jl(new)test/ext_distributed/reduce_distributedtest.jlreduce_distributedtest.jl(added missing imports required for the test to run at all).common.jl,optimizer.jl, andsynchronized.jlto_distributedtest.jlsuffix so they are picked up by the test runner. Also fixed pre-existing upstream breakage (missing imports).All tests pass with
mpiexecjl -n 2on CPU/MPI.Backward compatibility
All changes are additive or replace a broken code path with a working one.
resolve_unused_parameters!!is a new, optional public API — existingtraining loops that don't use conditional computation are unaffected.
The PMI check can be bypassed with
force=true.