Add DDP gradient-sync (all_reduce) timing via comm hook (TRA-16)#149
Open
Pendu wants to merge 10 commits into
Open
Add DDP gradient-sync (all_reduce) timing via comm hook (TRA-16)#149Pendu wants to merge 10 commits into
Pendu wants to merge 10 commits into
Conversation
…model auto-install gap)
…to-install; fix stale docs
…l chain + integration parity
…ep comm timing tables
…) in timing tables
…demo, fix stale docs
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.
What this adds
TraceML currently times dataloader, H2D, forward, backward, and optimizer, but is blind to the
biggest distributed-specific cost: DDP gradient synchronization. This PR adds per-bucket timing
of every DDP all_reduce during
loss.backward(), emitted as a new step-scoped event_traceml_comm:ddp_grad_sync, with zero changes to user training code.Why it matters: on a 2-node run (validation below) the per-step summary attributes the
all_reduce time to compute/wait, so a genuinely comm-bound run is indistinguishable from a
compute-bound one. This PR makes the comm signal exist as first-class data.
How it works
instrumentation/hooks/ddp_comm_hook.py: a comm-hook factory that wraps a base hook(default: PyTorch's
allreduce_hook) with a CUDA-event pair around each bucket's collective.gpu_startis recorded on the compute stream at hook entry;gpu_endin the future's.then()callback after NCCL completes. Events resolve asynchronously via the existingsampler path (no syncs on the hot path). The reduced bucket tensor passes through unchanged.
instrumentation/patches/ddp_comm_patch.py: a class-level patch onDistributedDataParallel.forward, installed once attraceml.init(), that lazily installsthe comm hook on each DDP instance's first forward. This mirrors the existing
patch_h2dpattern and makes installation structural rather than dependent on which object an
integration happens to pass around (the failure class from feat: add callback-based Hugging Face integration #135 / Harden instrumentation-state: make integration telemetry completeness self-verifying #141).
patch_ddp_commflag onTraceMLInitConfig, wired exactly likepatch_h2d(auto=on, manual=off, selective=opt-in). Explicit API:
traceml.wrap_ddp(model, base_hook=None).for any reason, TraceML warns on stderr and training proceeds untouched.
Scope: data-only (deliberate)
Events flow through the existing name-agnostic pipeline (sampler -> aggregator -> SQLite
step_time_samples.events_json) and are queryable today. They are NOT yet surfaced in theterminal/dashboard renderer or the diagnosis rules; that is a follow-up (internally TRA-30)
because the render layer is a closed allowlist and a comm bucket needs overlap-correction
(early buckets overlap backward compute) plus a comm-bound/comm-straggler rule.
Validation
tests/test_ddp_comm_hook.py(install/idempotency/fail-open, per-bucket emission,base_hook composition, CUDA event pool cleanup on error paths, value pass-through regression
guard, config parity, order-independent global-state handling). Two gated 2-rank gloo tests
(
TRACEML_RUN_DIST_TESTS=1): gradient numerical identity vs a no-hook reference, and thereal auto-install chain (
init(mode="auto")-> first forward -> hook installed -> events).wrap_ddpcall: hook installed on both ranks,per-bucket events on every step, gradients bit-identical across ranks.
traceml run(multi-node, aggregator on node 0): all 60/60 step rows(30 steps x 2 ranks) contain
_traceml_comm:ddp_grad_syncwith GPU-resolved durations inSQLite, alongside the five existing internal streams.
final_summary.jsoncontains no commkey, confirming the data-only boundary behaves as intended.
examples/ddp_comm_hook_demo.py(also listed in the examples README): runnable single- ormulti-node; prints a per-step table of all step phases (data/h2d/fwd/bwd/opt/comm/total) from
in-process data under plain
torchrun, or read back from the session DB undertraceml run.Overhead notes
Future.thencallback; nosynchronization is added to the training path.
fused copy+divide (documented in
reducer.cpp); gradients remain numerically identical(asserted in the gated test against a no-hook reference).
Known limitations (intentional, called out in docstrings)
state=None.fp16_compress_hookworks (verified).mode="auto"). Lightning and Rayintegrations do not enable
patch_ddp_commyet:TraceMLRayConfigcannot express the flag,and enabling it for Lightning alone would break re-init for the documented Ray+Lightning
selective recipe. Enabling both together is a small follow-up.
no_sync()accumulation steps DDP suppresses comm hooks, so those steps correctlyproduce zero comm events.
Notes for review
recommended, matching how recent PRs landed.
main.