[perf]: - #1630
Conversation
Expose PyTorch's CUDA fused AdamW through modular training config while preserving its current automatic default. Seed optimizer step state on-device when fused or capturable so distributed-checkpoint resume remains valid.
Merge Protections🔴 1 of 1 protections blocking · waiting on 👀 reviews and 🤖 CI
🔴 PR merge requirementsWaiting for
This rule is failing.
|
There was a problem hiding this comment.
Code Review
This pull request adds support for the fused AdamW optimizer parameter across configurations, parsing, and optimizer initialization. Feedback highlights a critical issue in seed_optimizer_state_for_resume where the step state tensor shape must be (1,) instead of () when fused or capturable is enabled to prevent shape mismatch errors during Distributed Checkpoint (DCP) restoration. It is also recommended to update the unit tests to assert these correct tensor shapes.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| step_device = p.device if fused or capturable else torch.device("cpu") | ||
| step_dtype = (torch.float64 | ||
| if not fused and torch.get_default_dtype() == torch.float64 else torch.float32) | ||
| opt.state[p] = { | ||
| "step": torch.tensor(0.0), | ||
| "step": torch.zeros((), dtype=step_dtype, device=step_device), | ||
| "exp_avg": torch.zeros_like(p), | ||
| "exp_avg_sq": torch.zeros_like(p), | ||
| } |
There was a problem hiding this comment.
When fused or capturable is enabled, PyTorch's AdamW (and Adam) initializes the step state tensor as a 1D tensor of shape (1,) (to allow in-place updates during CUDA graph replay or fused kernel execution). For the default (non-fused, non-capturable) path, it initializes step as a 0D scalar tensor of shape ().
Because Distributed Checkpoint (DCP) requires exact shape matching between the pre-seeded optimizer state and the saved checkpoint, initializing step as a 0D tensor when fused or capturable is enabled will cause a shape mismatch RuntimeError when resuming from a checkpoint.
Additionally, PyTorch forces step to be torch.float32 when either fused or capturable is enabled, so step_dtype should also check capturable to avoid using torch.float64 if the default dtype is set to float64.
| step_device = p.device if fused or capturable else torch.device("cpu") | |
| step_dtype = (torch.float64 | |
| if not fused and torch.get_default_dtype() == torch.float64 else torch.float32) | |
| opt.state[p] = { | |
| "step": torch.tensor(0.0), | |
| "step": torch.zeros((), dtype=step_dtype, device=step_device), | |
| "exp_avg": torch.zeros_like(p), | |
| "exp_avg_sq": torch.zeros_like(p), | |
| } | |
| step_device = p.device if fused or capturable else torch.device("cpu") | |
| step_dtype = (torch.float64 | |
| if not (fused or capturable) and torch.get_default_dtype() == torch.float64 else torch.float32) | |
| step_shape = (1,) if fused or capturable else () | |
| opt.state[p] = { | |
| "step": torch.zeros(step_shape, dtype=step_dtype, device=step_device), | |
| "exp_avg": torch.zeros_like(p), | |
| "exp_avg_sq": torch.zeros_like(p), | |
| } |
| def test_resume_seed_places_fused_step_with_parameter() -> None: | ||
| parameter = torch.nn.Parameter(torch.empty(1, device="meta")) | ||
| optimizer = torch.optim.AdamW([parameter], fused=True) | ||
| method = SimpleNamespace(get_optimizers=lambda _: [optimizer]) | ||
|
|
||
| TrainingMethod.seed_optimizer_state_for_resume(method) | ||
|
|
||
| assert optimizer.state[parameter]["step"].device == parameter.device | ||
| assert optimizer.state[parameter]["step"].dtype == torch.float32 | ||
|
|
||
|
|
||
| def test_resume_seed_keeps_default_step_on_cpu() -> None: | ||
| parameter = torch.nn.Parameter(torch.empty(1, device="meta")) | ||
| optimizer = torch.optim.AdamW([parameter]) | ||
| method = SimpleNamespace(get_optimizers=lambda _: [optimizer]) | ||
|
|
||
| TrainingMethod.seed_optimizer_state_for_resume(method) | ||
|
|
||
| assert optimizer.state[parameter]["step"].device.type == "cpu" |
There was a problem hiding this comment.
Update the resume seeding tests to assert the correct tensor shapes for both the fused/capturable ((1,)) and default (()) optimizer step states to prevent future regressions.
def test_resume_seed_places_fused_step_with_parameter() -> None:
parameter = torch.nn.Parameter(torch.empty(1, device="meta"))
optimizer = torch.optim.AdamW([parameter], fused=True)
method = SimpleNamespace(get_optimizers=lambda _: [optimizer])
TrainingMethod.seed_optimizer_state_for_resume(method)
assert optimizer.state[parameter]["step"].device == parameter.device
assert optimizer.state[parameter]["step"].dtype == torch.float32
assert optimizer.state[parameter]["step"].shape == (1,)
def test_resume_seed_keeps_default_step_on_cpu() -> None:
parameter = torch.nn.Parameter(torch.empty(1, device="meta"))
optimizer = torch.optim.AdamW([parameter])
method = SimpleNamespace(get_optimizers=lambda _: [optimizer])
TrainingMethod.seed_optimizer_state_for_resume(method)
assert optimizer.state[parameter]["step"].device.type == "cpu"
assert optimizer.state[parameter]["step"].shape == ()…FLOP/s convention
…6 campaign handoff
…lthy-allocation gate required
…as forward-schedule intrinsic
|
This PR has merge conflicts with the base branch. Please rebase: git fetch origin main
git rebase origin/main
# Resolve any conflicts, then:
git push --force-with-lease |
Summary
This PR is the cumulative LTX-2 BF16 training-efficiency stack. It keeps registered/master parameters and Adam state in FP32, uses BF16 only for gathered working parameters, compute, gradients, and reductions, and adds no dependency.
Current tracker head:
52f1114dd95934903e49620338650780d8152b8dMeasured optimization head:
20c36acefc97e8b743f79a5c52883561853a7d85Final validation commits:
0e60a0e9ccbd0c62d730fcfcf78f7d4a4d0554e7,3f3f06541c1a4ab01ddb81c4840d42040fd02a38The optimized recipe reaches 40.810314% MFU on 4x GB200 and 43.623053% MFU on the healthy 8x/two-tray allocation. A replacement 8x allocation was power/clock limited and measured 30.478319% MFU; both 8x results are reported because the absolute difference is allocation-specific, not attributable to a source change. The 50% target is not reached.
What changed
overfit_ltx2_t2v.yaml; defaults preserve existing behavior for other recipes.Ongoing experiment tracker
This PR and branch are now the cumulative scratch space for MFU optimization attempts. Later we will extract focused, independently mergeable PRs from it.
scripts/train/ltx2_mfu/README.mdis the resume handoff, benchmark contract, current stopping point, and decision index.REPORT.mdpreserves the chronological accepted/rejected experiment history.harness/,runners/,probes/, andreports/preserve the generated source-like artifacts.run_current.shis the maintained 4x/8x MFU entrypoint;run_observed.shis the maintained 4x W&B/validation entrypoint.Final MFU
Common contract:
FastVideo/LTX2-Distilled-Diffusers, 81x480x832, dense FA4, regional compile, fused AdamW, repeated/prefetched input, singleton timestep, no validation inside the MFU timer, and the slowest-rank median of 10 warmup + 20 measured steps. Completed rows use no activation checkpointing; the 1x capacity audit used full checkpointing. MFU uses 0.353881 PFLOP/sample and NVIDIA's 2.450 PFLOP/s dense BF16 peak per GB200.20c36acefThe 4x B2 result improves over equal-global-batch B1/gradient-accumulation-2 controls at 0.743365705741 s / 38.861435% MFU: -35.499791 ms / -4.775549%, +5.014942% throughput, +1.948878 MFU points, with 0.198028% control drift.
On the degraded 8x pair, group 2 is neutral against its same-pair 1.419745997933 s / 30.521241% control midpoint: +1.997281 ms / +0.140679% and -0.042922 MFU points, smaller than the 0.245257% control drift. The pair nevertheless passed an eight-rank MNNVL/NVLS health gate at 463.195 GB/s derived bus bandwidth; telemetry showed clocks capped near 1200 MHz. The healthy and degraded rows must not be used as a source A/B comparison.
Validation
20c36acef; those bytes are committed at final source head3f3f06541. W&B runiopu4dwm; log SHA-256d84ae1a648d37d413a8e6116dbbad855d62cec0f30c3e016a506a4b4fb17c5f1.645c68e27f3967b8352efdc2ed4690a59a66cad5b644f41350ee7b0acb4df75e.aa260691ce2847aa1e5cf5c852fec8c2af67de306b4bf393557bdbc3a4477d54.5e1f03976d834465cd60072459f960f40b4d0ac4dfba7d24e7b4856f4e615bccand65ea51b8b98c21da82eb0a93d64dbc341b748dc5ecefdf6b4d2f033de56a4324.0277cbcaac04bac957bf1cfda000a365ccde4a15b5ae7b947b61c2a4237baefdand30c058a42a9f35329ec191afba476aed5f1b3f3d33b4957046a6c384eab7d82e.Risks and rollback