Feat/cuda graph training - #671
Merged
Merged
Conversation
train(cuda_graph=True) captures the network forward/backward with torch.cuda.make_graphed_callables while the loss, optimizer, EMA and LR schedule stay eager. Dispatch is shape-keyed: one graph is captured once a batch shape repeats, and any other shape (multi-scale batches, last partial batch) runs eager, so enabling the flag never changes numerics. Unsupported setups (non-CUDA, DDP, distillation, families or tasks without a capture spec) fall back to eager with one warning. Families wire in through cuda_graph_train_spec: yolo9 (detect, exact DDetect head, includes yolo9_p2) graphs the whole network and reuses the head loss; rfdetr (detect) graphs the full LWDETR network, whose training forward never reads targets, and keeps the Hungarian criterion eager. Capture-enabling fixes that also stand alone: - rfdetr transformer: cache the per-resolution spatial_shapes tensor instead of writing Python ints element-wise into a CUDA tensor each forward (unpinned H2D copies, illegal during capture), and skip the int() readback sanity assert only while a stream is capturing. - trainer loop: drop total_loss_raw with outputs/loss at iteration end so no autograd-graph reference survives the step; live AccumulateGrad nodes bound to the default stream invalidate later capture. Measured on an RTX 5070 Ti (AMP, 640px, no dataloader bottleneck): yolo9-t b16 1.56x, yolo9-t b8 2.25x, yolo9-s b16 1.23x, yolo9-m b8 1.10x steps/s. Parity gated in tests/unit/test_cuda_graph_training.py: yolo9 bit-identical loss and parameters vs eager; rfdetr step-0 bit-identical and trajectory within its eager run-to-run atomics noise.
The trainer's _autocast_context now forwards cache_enabled (disabled only while a CUDA graph training manager is active); the fake autocast in the dtype test needed the keyword, and both states are now asserted.
make_graphed_callables warm-up runs extra forward passes on the live model, advancing BatchNorm running stats past what one eager step performs, so validation, EMA and checkpoints would drift from eager. The manager now snapshots every network buffer before capture and restores it in place afterwards (copy_ keeps the addresses the captured kernels recorded), on both the success and failure paths; the replay for the capture batch then applies that batch's single update exactly as eager would. The YOLO9 parity test now also asserts bit-equal buffers after six steps, and fails if the restore is removed.
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.
Code provenance
Original implementation written for this PR. CUDA graph capture is built entirely on PyTorch public API (torch.cuda.make_graphed_callables, torch.cuda.is_current_stream_capturing) following PyTorch documented capture recipes. No third-party training framework code was consulted or referenced.
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.Greptile Summary
Implements opt-in, single-GPU CUDA graph training while retaining eager loss computation and fallback behavior.
Confidence Score: 5/5
The PR appears safe to merge.
No blocking failure remains; the previously reported BatchNorm warm-up drift is addressed by snapshotting buffers before capture, restoring them in place before the first replay, and testing exact YOLO9 buffer parity.
Important Files Changed
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD Batch[Training batch] --> Enabled{CUDA graph enabled and supported?} Enabled -- No --> Eager[Eager model forward] Enabled -- Yes --> Shape{Captured shape?} Shape -- No --> Warmup{Shape repeated enough?} Warmup -- No --> Eager Warmup -- Yes --> Snapshot[Snapshot model buffers] Snapshot --> Capture[Warm up and capture network] Capture --> Restore[Restore buffers in place] Restore --> Replay[Replay captured network] Shape -- Yes --> Replay Replay --> Loss[Eager family loss] Eager --> Loss Loss --> Backward[Backward and optimizer step]Reviews (2): Last reviewed commit: "Restore module buffers after capture war..." | Re-trigger Greptile
Context used (3)