Skip to content

[graph_trainer] Add CooR precompile support for aot_fx_trace compile mode#2975

Merged
bobrenjc93 merged 12 commits into
mainfrom
gh/bobrenjc93/41/head
Apr 16, 2026
Merged

[graph_trainer] Add CooR precompile support for aot_fx_trace compile mode#2975
bobrenjc93 merged 12 commits into
mainfrom
gh/bobrenjc93/41/head

Conversation

@bobrenjc93

@bobrenjc93 bobrenjc93 commented Apr 15, 2026

Copy link
Copy Markdown
Contributor

Stack from ghstack (oldest at bottom):

Introduce aot_fx_trace as an alternate precompilation path alongside
the existing AOT joint graph export. aot_fx_trace uses make_fx to
trace the full fwd+loss+bwd step as a single FX graph, then serializes
the GraphModule for loading on all ranks during training. Because
cudagraphs is a default pass, we also include cudagraph composability
related changes in this PR to get to bitwise equivalence between
aot_fx_trace with and without precompile.

Changes:

  • precompile_main.py: Refactor into _common_setup() + _precompile_aot()
    • _precompile_aot_fx_trace(), route by --compile.mode. Use Python
      float for global_valid_tokens (matching dist_sum's return type) so
      make_fx bakes it as a graph constant, identical to the runtime path.
  • precompile.py: PrecompiledFxTraceArtifact dataclass, save/load fns.
    Uses GraphPickler (not plain pickle) to preserve SymInt expressions
    (_runtime_compute_coordinate_on_dim) through serialization — plain
    pickle evaluates SymInts to concrete trace-time values, baking in
    rank-specific embedding vocab offsets.
  • compile.py: Mode-aware validation (aot_fx_trace doesn't need
    serializable passes), logging for precompile load vs runtime trace
  • trainer.py: _load_precompiled_fx_trace(), branching in
    _make_fx_forward_backward_step()
  • make_fx_tracer.py: Use set_multithreading_enabled(False) so backward
    tracing runs on the calling thread, keeping the compile_on_one_rank
    ContextVar visible
  • cudagraph.py: Defer _input_indices_to_copy when example_inputs is
    empty (precompile load path), accept opaque values (DeviceMesh) in
    input validation, skip non-tensors in static address checks.
    Squashed the separate cudagraph CooR PR into this one because
    cudagraph activates automatically for aot_fx_trace graphs (all
    inputs are desugared plain tensors + opaques, no HOPs), so bitwise
    equivalence testing required both fixes together.
  • passes.py: Guard cudagraph_pass against non-GraphModule inputs

Test plan

  • pytest torchtitan/experiments/graph_trainer/tests/test_bitwise_deterministic.py -x — 6 passed, 4 skipped (Llama3, DSv3 × {eager self-deterministic, eager vs aot_fx_trace, precompile vs trace})
  • pytest torchtitan/experiments/graph_trainer/tests/test_precompile.py -x — all pass
  • pytest torchtitan/experiments/graph_trainer/tests/test_trace_module.py -x — all pass
  • 8-GPU integration tests via CI (ciflow/8gpu)

bobrenjc93 added a commit that referenced this pull request Apr 15, 2026
…mode

Introduce aot_fx_trace as an alternate precompilation path alongside
the existing AOT joint graph export. aot_fx_trace uses make_fx to
trace the full fwd+loss+bwd step as a single FX graph, then serializes
the GraphModule for loading on all ranks during training. Because
cudagraphs is a default pass, we also include cudagraph composability
related changes in this PR to get to bitwise equivalence between
aot_fx_trace with and without precompile.

Changes:
- precompile_main.py: Refactor into _common_setup() + _precompile_aot()
  + _precompile_aot_fx_trace(), route by --compile.mode. Use Python
  float for global_valid_tokens (matching dist_sum's return type) so
  make_fx bakes it as a graph constant, identical to the runtime path.
- precompile.py: PrecompiledFxTraceArtifact dataclass, save/load fns.
  Uses GraphPickler (not plain pickle) to preserve SymInt expressions
  (_runtime_compute_coordinate_on_dim) through serialization — plain
  pickle evaluates SymInts to concrete trace-time values, baking in
  rank-specific embedding vocab offsets.
- compile.py: Mode-aware validation (aot_fx_trace doesn't need
  serializable passes), logging for precompile load vs runtime trace
- trainer.py: _load_precompiled_fx_trace(), branching in
  _make_fx_forward_backward_step()
- make_fx_tracer.py: Use set_multithreading_enabled(False) so backward
  tracing runs on the calling thread, keeping the compile_on_one_rank
  ContextVar visible
- cudagraph.py: Defer _input_indices_to_copy when example_inputs is
  empty (precompile load path), accept opaque values (DeviceMesh) in
  input validation, skip non-tensors in static address checks.
  Squashed the separate cudagraph CooR PR into this one because
  cudagraph activates automatically for aot_fx_trace graphs (all
  inputs are desugared plain tensors + opaques, no HOPs), so bitwise
  equivalence testing required both fixes together.
- passes.py: Guard cudagraph_pass against non-GraphModule inputs

Numerics (llama3 debugmodel, 8xH100, FSDP4+TP2, 50 steps, deterministic):
- 50/50 steps BITWISE IDENTICAL between precompile and non-precompile
- Loss/grad_norm/MFU: https://pxl.cl/9pNvp
- Cudagraph profile: https://pxl.cl/9pNB0


ghstack-source-id: 5cfa431
Pull-Request: #2975
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Apr 15, 2026
…mode

Introduce aot_fx_trace as an alternate precompilation path alongside
the existing AOT joint graph export. aot_fx_trace uses make_fx to
trace the full fwd+loss+bwd step as a single FX graph, then serializes
the GraphModule for loading on all ranks during training.

Key differences from AOT precompile:
- Traces fwd+loss+bwd together (no AOTAutograd partitioning)
- Serializes the FX graph, not compiled Triton kernels
- Inductor compilation happens at load time on each rank
- No pipeline parallel support (no graph partitioning)

Changes:
- precompile_main.py: Refactor into _common_setup() + _precompile_aot()
  + _precompile_aot_fx_trace(), route by --compile.mode. Use Python
  float for global_valid_tokens (matching dist_sum's return type) so
  make_fx bakes it as a graph constant, identical to the runtime path.
- precompile.py: PrecompiledFxTraceArtifact dataclass, save/load fns.
  Uses GraphPickler (not plain pickle) to preserve SymInt expressions
  (_runtime_compute_coordinate_on_dim) through serialization — plain
  pickle evaluates SymInts to concrete trace-time values, baking in
  rank-specific embedding vocab offsets.
- compile.py: Mode-aware validation (aot_fx_trace doesn't need
  serializable passes), logging for precompile load vs runtime trace
- trainer.py: _load_precompiled_fx_trace(), branching in
  _make_fx_forward_backward_step()
- make_fx_tracer.py: Use set_multithreading_enabled(False) so backward
  tracing runs on the calling thread, keeping the compile_on_one_rank
  ContextVar visible
- cudagraph.py: Defer _input_indices_to_copy when example_inputs is
  empty (precompile load path), accept opaque values (DeviceMesh) in
  input validation, skip non-tensors in static address checks.
  Squashed the separate cudagraph CooR PR into this one because
  cudagraph activates automatically for aot_fx_trace graphs (all
  inputs are desugared plain tensors + opaques, no HOPs), so bitwise
  equivalence testing required both fixes together.
- passes.py: Guard cudagraph_pass against non-GraphModule inputs

Test plan:
- pytest torchtitan/experiments/graph_trainer/tests/test_bitwise_deterministic.py -x
  — 6 passed, 4 skipped (Llama3, DSv3 × {eager self-deterministic,
  eager vs aot_fx_trace, precompile vs trace})
- pytest torchtitan/experiments/graph_trainer/tests/test_precompile.py -x — all pass
- pytest torchtitan/experiments/graph_trainer/tests/test_trace_module.py -x — all pass
- 8-GPU integration tests via CI (ciflow/8gpu)

ghstack-source-id: 3691eb1
Pull-Request: #2975
@bobrenjc93 bobrenjc93 force-pushed the gh/bobrenjc93/41/head branch from 0815019 to ba1cf36 Compare April 15, 2026 21:21
@bobrenjc93 bobrenjc93 force-pushed the gh/bobrenjc93/41/base branch from 2f733f7 to 98ec7b4 Compare April 15, 2026 21:21
[ghstack-poisoned]
[ghstack-poisoned]
bobrenjc93 added a commit that referenced this pull request Apr 15, 2026
…mode

Introduce aot_fx_trace as an alternate precompilation path alongside
the existing AOT joint graph export. aot_fx_trace uses make_fx to
trace the full fwd+loss+bwd step as a single FX graph, then serializes
the GraphModule for loading on all ranks during training.

Key differences from AOT precompile:
- Traces fwd+loss+bwd together (no AOTAutograd partitioning)
- Serializes the FX graph, not compiled Triton kernels
- Inductor compilation happens at load time on each rank
- No pipeline parallel support (no graph partitioning)

Changes:
- precompile_main.py: Refactor into _common_setup() + _precompile_aot()
  + _precompile_aot_fx_trace(), route by --compile.mode. Use Python
  float for global_valid_tokens (matching dist_sum's return type) so
  make_fx bakes it as a graph constant, identical to the runtime path.
- precompile.py: PrecompiledFxTraceArtifact dataclass, save/load fns.
  Uses GraphPickler (not plain pickle) to preserve SymInt expressions
  (_runtime_compute_coordinate_on_dim) through serialization — plain
  pickle evaluates SymInts to concrete trace-time values, baking in
  rank-specific embedding vocab offsets.
- compile.py: Mode-aware validation (aot_fx_trace doesn't need
  serializable passes), logging for precompile load vs runtime trace
- trainer.py: _load_precompiled_fx_trace(), branching in
  _make_fx_forward_backward_step()
- make_fx_tracer.py: Use set_multithreading_enabled(False) so backward
  tracing runs on the calling thread, keeping the compile_on_one_rank
  ContextVar visible
- cudagraph.py: Defer _input_indices_to_copy when example_inputs is
  empty (precompile load path), accept opaque values (DeviceMesh) in
  input validation, skip non-tensors in static address checks.
  Squashed the separate cudagraph CooR PR into this one because
  cudagraph activates automatically for aot_fx_trace graphs (all
  inputs are desugared plain tensors + opaques, no HOPs), so bitwise
  equivalence testing required both fixes together.
- passes.py: Guard cudagraph_pass against non-GraphModule inputs

Test plan:
- pytest torchtitan/experiments/graph_trainer/tests/test_bitwise_deterministic.py -x
  — 6 passed, 4 skipped (Llama3, DSv3 × {eager self-deterministic,
  eager vs aot_fx_trace, precompile vs trace})
- pytest torchtitan/experiments/graph_trainer/tests/test_precompile.py -x — all pass
- pytest torchtitan/experiments/graph_trainer/tests/test_trace_module.py -x — all pass
- 8-GPU integration tests via CI (ciflow/8gpu)

ghstack-source-id: 82fc5d8
Pull-Request: #2975
[ghstack-poisoned]
[ghstack-poisoned]
[ghstack-poisoned]
[ghstack-poisoned]
[ghstack-poisoned]
bobrenjc93 added a commit that referenced this pull request Apr 15, 2026
…mode

Introduce aot_fx_trace as an alternate precompilation path alongside
the existing AOT joint graph export. aot_fx_trace uses make_fx to
trace the full fwd+loss+bwd step as a single FX graph, then serializes
the GraphModule for loading on all ranks during training.

Key differences from AOT precompile:
- Traces fwd+loss+bwd together (no AOTAutograd partitioning)
- Serializes the FX graph, not compiled Triton kernels
- Inductor compilation happens at load time on each rank
- No pipeline parallel support (no graph partitioning)

Changes:
- precompile_main.py: Refactor into _common_setup() + _precompile_aot()
  + _precompile_aot_fx_trace(), route by --compile.mode. Use Python
  float for global_valid_tokens (matching dist_sum's return type) so
  make_fx bakes it as a graph constant, identical to the runtime path.
- precompile.py: PrecompiledFxTraceArtifact dataclass, save/load fns.
  Uses GraphPickler (not plain pickle) to preserve SymInt expressions
  (_runtime_compute_coordinate_on_dim) through serialization — plain
  pickle evaluates SymInts to concrete trace-time values, baking in
  rank-specific embedding vocab offsets.
- compile.py: Mode-aware validation (aot_fx_trace doesn't need
  serializable passes), logging for precompile load vs runtime trace
- trainer.py: _load_precompiled_fx_trace(), branching in
  _make_fx_forward_backward_step()
- make_fx_tracer.py: Use set_multithreading_enabled(False) so backward
  tracing runs on the calling thread, keeping the compile_on_one_rank
  ContextVar visible
- cudagraph.py: Defer _input_indices_to_copy when example_inputs is
  empty (precompile load path), accept opaque values (DeviceMesh) in
  input validation, skip non-tensors in static address checks.
  Squashed the separate cudagraph CooR PR into this one because
  cudagraph activates automatically for aot_fx_trace graphs (all
  inputs are desugared plain tensors + opaques, no HOPs), so bitwise
  equivalence testing required both fixes together.
- passes.py: Guard cudagraph_pass against non-GraphModule inputs

Test plan:
- pytest torchtitan/experiments/graph_trainer/tests/test_bitwise_deterministic.py -x
  — 6 passed, 4 skipped (Llama3, DSv3 × {eager self-deterministic,
  eager vs aot_fx_trace, precompile vs trace})
- pytest torchtitan/experiments/graph_trainer/tests/test_precompile.py -x — all pass
- pytest torchtitan/experiments/graph_trainer/tests/test_trace_module.py -x — all pass
- 8-GPU integration tests via CI (ciflow/8gpu)

ghstack-source-id: 44abd37
Pull-Request: #2975
@bobrenjc93 bobrenjc93 marked this pull request as ready for review April 15, 2026 21:55
Comment thread torchtitan/experiments/graph_trainer/cudagraph.py Outdated
Comment thread torchtitan/experiments/graph_trainer/cudagraph.py Outdated
Comment thread torchtitan/experiments/graph_trainer/passes.py
Comment thread torchtitan/experiments/graph_trainer/precompile.py
[ghstack-poisoned]
bobrenjc93 added a commit that referenced this pull request Apr 16, 2026
…mode

Introduce aot_fx_trace as an alternate precompilation path alongside
the existing AOT joint graph export. aot_fx_trace uses make_fx to
trace the full fwd+loss+bwd step as a single FX graph, then serializes
the GraphModule for loading on all ranks during training.

Key differences from AOT precompile:
- Traces fwd+loss+bwd together (no AOTAutograd partitioning)
- Serializes the FX graph, not compiled Triton kernels
- Inductor compilation happens at load time on each rank
- No pipeline parallel support (no graph partitioning)

Changes:
- precompile_main.py: Refactor into _common_setup() + _precompile_aot()
  + _precompile_aot_fx_trace(), route by --compile.mode. Use Python
  float for global_valid_tokens (matching dist_sum's return type) so
  make_fx bakes it as a graph constant, identical to the runtime path.
- precompile.py: PrecompiledFxTraceArtifact dataclass, save/load fns.
  Uses GraphPickler (not plain pickle) to preserve SymInt expressions
  (_runtime_compute_coordinate_on_dim) through serialization — plain
  pickle evaluates SymInts to concrete trace-time values, baking in
  rank-specific embedding vocab offsets.
- compile.py: Mode-aware validation (aot_fx_trace doesn't need
  serializable passes), logging for precompile load vs runtime trace
- trainer.py: _load_precompiled_fx_trace(), branching in
  _make_fx_forward_backward_step()
- make_fx_tracer.py: Use set_multithreading_enabled(False) so backward
  tracing runs on the calling thread, keeping the compile_on_one_rank
  ContextVar visible
- cudagraph.py: Defer _input_indices_to_copy when example_inputs is
  empty (precompile load path), accept opaque values (DeviceMesh) in
  input validation, skip non-tensors in static address checks.
  Squashed the separate cudagraph CooR PR into this one because
  cudagraph activates automatically for aot_fx_trace graphs (all
  inputs are desugared plain tensors + opaques, no HOPs), so bitwise
  equivalence testing required both fixes together.
- passes.py: Guard cudagraph_pass against non-GraphModule inputs

Test plan:
- pytest torchtitan/experiments/graph_trainer/tests/test_bitwise_deterministic.py -x
  — 6 passed, 4 skipped (Llama3, DSv3 × {eager self-deterministic,
  eager vs aot_fx_trace, precompile vs trace})
- pytest torchtitan/experiments/graph_trainer/tests/test_precompile.py -x — all pass
- pytest torchtitan/experiments/graph_trainer/tests/test_trace_module.py -x — all pass
- 8-GPU integration tests via CI (ciflow/8gpu)

ghstack-source-id: 23462f9
Pull-Request: #2975
Comment thread torchtitan/experiments/graph_trainer/make_fx_tracer.py
Comment thread torchtitan/experiments/graph_trainer/passes.py Outdated
Comment thread torchtitan/experiments/graph_trainer/passes.py Outdated
Comment thread torchtitan/experiments/graph_trainer/passes.py
Comment thread torchtitan/experiments/graph_trainer/trainer.py Outdated
Comment thread torchtitan/experiments/graph_trainer/trainer.py Outdated
Comment thread torchtitan/experiments/graph_trainer/trainer.py
Comment thread torchtitan/experiments/graph_trainer/precompile.py
Comment thread torchtitan/experiments/graph_trainer/precompile.py Outdated
Comment thread torchtitan/experiments/graph_trainer/cudagraph.py
Comment thread torchtitan/experiments/graph_trainer/cudagraph.py
Comment thread torchtitan/experiments/graph_trainer/precompile_main.py Outdated
[ghstack-poisoned]
bobrenjc93 added a commit that referenced this pull request Apr 16, 2026
…mode

Introduce aot_fx_trace as an alternate precompilation path alongside
the existing AOT joint graph export. aot_fx_trace uses make_fx to
trace the full fwd+loss+bwd step as a single FX graph, then serializes
the GraphModule for loading on all ranks during training.

Key differences from AOT precompile:
- Traces fwd+loss+bwd together (no AOTAutograd partitioning)
- Serializes the FX graph, not compiled Triton kernels
- Inductor compilation happens at load time on each rank
- No pipeline parallel support (no graph partitioning)

Changes:
- precompile_main.py: Refactor into _common_setup() + _precompile_aot()
  + _precompile_aot_fx_trace(), route by --compile.mode. Use Python
  float for global_valid_tokens (matching dist_sum's return type) so
  make_fx bakes it as a graph constant, identical to the runtime path.
- precompile.py: PrecompiledFxTraceArtifact dataclass, save/load fns.
  Uses GraphPickler (not plain pickle) to preserve SymInt expressions
  (_runtime_compute_coordinate_on_dim) through serialization — plain
  pickle evaluates SymInts to concrete trace-time values, baking in
  rank-specific embedding vocab offsets.
- compile.py: Mode-aware validation (aot_fx_trace doesn't need
  serializable passes), logging for precompile load vs runtime trace
- trainer.py: _load_precompiled_fx_trace(), branching in
  _make_fx_forward_backward_step()
- make_fx_tracer.py: Use set_multithreading_enabled(False) so backward
  tracing runs on the calling thread, keeping the compile_on_one_rank
  ContextVar visible
- cudagraph.py: Defer _input_indices_to_copy when example_inputs is
  empty (precompile load path), accept opaque values (DeviceMesh) in
  input validation, skip non-tensors in static address checks.
  Squashed the separate cudagraph CooR PR into this one because
  cudagraph activates automatically for aot_fx_trace graphs (all
  inputs are desugared plain tensors + opaques, no HOPs), so bitwise
  equivalence testing required both fixes together.
- passes.py: Guard cudagraph_pass against non-GraphModule inputs

Test plan:
- pytest torchtitan/experiments/graph_trainer/tests/test_bitwise_deterministic.py -x
  — 6 passed, 4 skipped (Llama3, DSv3 × {eager self-deterministic,
  eager vs aot_fx_trace, precompile vs trace})
- pytest torchtitan/experiments/graph_trainer/tests/test_precompile.py -x — all pass
- pytest torchtitan/experiments/graph_trainer/tests/test_trace_module.py -x — all pass
- 8-GPU integration tests via CI (ciflow/8gpu)

ghstack-source-id: 509c94b
Pull-Request: #2975
[ghstack-poisoned]
bobrenjc93 added a commit that referenced this pull request Apr 16, 2026
…mode

Introduce aot_fx_trace as an alternate precompilation path alongside
the existing AOT joint graph export. aot_fx_trace uses make_fx to
trace the full fwd+loss+bwd step as a single FX graph, then serializes
the GraphModule for loading on all ranks during training.

Key differences from AOT precompile:
- Traces fwd+loss+bwd together (no AOTAutograd partitioning)
- Serializes the FX graph, not compiled Triton kernels
- Inductor compilation happens at load time on each rank
- No pipeline parallel support (no graph partitioning)

Changes:
- precompile_main.py: Refactor into _common_setup() + _precompile_aot()
  + _precompile_aot_fx_trace(), route by --compile.mode. Use Python
  float for global_valid_tokens (matching dist_sum's return type) so
  make_fx bakes it as a graph constant, identical to the runtime path.
- precompile.py: PrecompiledFxTraceArtifact dataclass, save/load fns.
  Uses GraphPickler (not plain pickle) to preserve SymInt expressions
  (_runtime_compute_coordinate_on_dim) through serialization — plain
  pickle evaluates SymInts to concrete trace-time values, baking in
  rank-specific embedding vocab offsets.
- compile.py: Mode-aware validation (aot_fx_trace doesn't need
  serializable passes), logging for precompile load vs runtime trace
- trainer.py: _load_precompiled_fx_trace(), branching in
  _make_fx_forward_backward_step()
- make_fx_tracer.py: Use set_multithreading_enabled(False) so backward
  tracing runs on the calling thread, keeping the compile_on_one_rank
  ContextVar visible
- cudagraph.py: Defer _input_indices_to_copy when example_inputs is
  empty (precompile load path), accept opaque values (DeviceMesh) in
  input validation, skip non-tensors in static address checks.
  Squashed the separate cudagraph CooR PR into this one because
  cudagraph activates automatically for aot_fx_trace graphs (all
  inputs are desugared plain tensors + opaques, no HOPs), so bitwise
  equivalence testing required both fixes together.
- passes.py: Guard cudagraph_pass against non-GraphModule inputs

Test plan:
- pytest torchtitan/experiments/graph_trainer/tests/test_bitwise_deterministic.py -x
  — 6 passed, 4 skipped (Llama3, DSv3 × {eager self-deterministic,
  eager vs aot_fx_trace, precompile vs trace})
- pytest torchtitan/experiments/graph_trainer/tests/test_precompile.py -x — all pass
- pytest torchtitan/experiments/graph_trainer/tests/test_trace_module.py -x — all pass
- 8-GPU integration tests via CI (ciflow/8gpu)

ghstack-source-id: 8541884
Pull-Request: #2975
[ghstack-poisoned]
bobrenjc93 added a commit that referenced this pull request Apr 16, 2026
…mode

Introduce aot_fx_trace as an alternate precompilation path alongside
the existing AOT joint graph export. aot_fx_trace uses make_fx to
trace the full fwd+loss+bwd step as a single FX graph, then serializes
the GraphModule for loading on all ranks during training.

Key differences from AOT precompile:
- Traces fwd+loss+bwd together (no AOTAutograd partitioning)
- Serializes the FX graph, not compiled Triton kernels
- Inductor compilation happens at load time on each rank
- No pipeline parallel support (no graph partitioning)

Changes:
- precompile_main.py: Refactor into _common_setup() + _precompile_aot()
  + _precompile_aot_fx_trace(), route by --compile.mode. Use Python
  float for global_valid_tokens (matching dist_sum's return type) so
  make_fx bakes it as a graph constant, identical to the runtime path.
- precompile.py: PrecompiledFxTraceArtifact dataclass, save/load fns.
  Uses GraphPickler (not plain pickle) to preserve SymInt expressions
  (_runtime_compute_coordinate_on_dim) through serialization — plain
  pickle evaluates SymInts to concrete trace-time values, baking in
  rank-specific embedding vocab offsets.
- compile.py: Mode-aware validation (aot_fx_trace doesn't need
  serializable passes), logging for precompile load vs runtime trace
- trainer.py: _load_precompiled_fx_trace(), branching in
  _make_fx_forward_backward_step()
- make_fx_tracer.py: Use set_multithreading_enabled(False) so backward
  tracing runs on the calling thread, keeping the compile_on_one_rank
  ContextVar visible
- cudagraph.py: Defer _input_indices_to_copy when example_inputs is
  empty (precompile load path), accept opaque values (DeviceMesh) in
  input validation, skip non-tensors in static address checks.
  Squashed the separate cudagraph CooR PR into this one because
  cudagraph activates automatically for aot_fx_trace graphs (all
  inputs are desugared plain tensors + opaques, no HOPs), so bitwise
  equivalence testing required both fixes together.
- passes.py: Guard cudagraph_pass against non-GraphModule inputs

Test plan:
- pytest torchtitan/experiments/graph_trainer/tests/test_bitwise_deterministic.py -x
  — 6 passed, 4 skipped (Llama3, DSv3 × {eager self-deterministic,
  eager vs aot_fx_trace, precompile vs trace})
- pytest torchtitan/experiments/graph_trainer/tests/test_precompile.py -x — all pass
- pytest torchtitan/experiments/graph_trainer/tests/test_trace_module.py -x — all pass
- 8-GPU integration tests via CI (ciflow/8gpu)

ghstack-source-id: 80c1fad
Pull-Request: #2975
Comment thread torchtitan/experiments/graph_trainer/passes.py
Comment thread torchtitan/experiments/graph_trainer/trainer.py
@SherlockNoMad

Copy link
Copy Markdown
Contributor

Do we have clear instruction in claude.md for agent to use the pre-compile workflow?

I would validate by prompting ”run graph_trainer precompile training of llama_8b for me, show me the MFU, profiling result and memory snapshot as proof of work" in a /new session.

this should work in one shot.
If it doesn't, "reflect on instruction from claude.md, improve based on the experiment you just did"

bobrenjc93 added a commit that referenced this pull request Apr 16, 2026
…mode

Introduce aot_fx_trace as an alternate precompilation path alongside
the existing AOT joint graph export. aot_fx_trace uses make_fx to
trace the full fwd+loss+bwd step as a single FX graph, then serializes
the GraphModule for loading on all ranks during training.

Key differences from AOT precompile:
- Traces fwd+loss+bwd together (no AOTAutograd partitioning)
- Serializes the FX graph, not compiled Triton kernels
- Inductor compilation happens at load time on each rank
- No pipeline parallel support (no graph partitioning)

Changes:
- precompile_main.py: Refactor into _common_setup() + _precompile_aot()
  + _precompile_aot_fx_trace(), route by --compile.mode. Use Python
  float for global_valid_tokens (matching dist_sum's return type) so
  make_fx bakes it as a graph constant, identical to the runtime path.
- precompile.py: PrecompiledFxTraceArtifact dataclass, save/load fns.
  Uses GraphPickler (not plain pickle) to preserve SymInt expressions
  (_runtime_compute_coordinate_on_dim) through serialization — plain
  pickle evaluates SymInts to concrete trace-time values, baking in
  rank-specific embedding vocab offsets.
- compile.py: Mode-aware validation (aot_fx_trace doesn't need
  serializable passes), logging for precompile load vs runtime trace
- trainer.py: _load_precompiled_fx_trace(), branching in
  _make_fx_forward_backward_step()
- make_fx_tracer.py: Use set_multithreading_enabled(False) so backward
  tracing runs on the calling thread, keeping the compile_on_one_rank
  ContextVar visible
- cudagraph.py: Defer _input_indices_to_copy when example_inputs is
  empty (precompile load path), accept opaque values (DeviceMesh) in
  input validation, skip non-tensors in static address checks.
  Squashed the separate cudagraph CooR PR into this one because
  cudagraph activates automatically for aot_fx_trace graphs (all
  inputs are desugared plain tensors + opaques, no HOPs), so bitwise
  equivalence testing required both fixes together.
- passes.py: Guard cudagraph_pass against non-GraphModule inputs

Test plan:
- pytest torchtitan/experiments/graph_trainer/tests/test_bitwise_deterministic.py -x
  — 6 passed, 4 skipped (Llama3, DSv3 × {eager self-deterministic,
  eager vs aot_fx_trace, precompile vs trace})
- pytest torchtitan/experiments/graph_trainer/tests/test_precompile.py -x — all pass
- pytest torchtitan/experiments/graph_trainer/tests/test_trace_module.py -x — all pass
- 8-GPU integration tests via CI (ciflow/8gpu)

ghstack-source-id: 80c1fad
Pull-Request: #2975
@bobrenjc93

Copy link
Copy Markdown
Contributor Author

For posterity - claude one shotted it

CleanShot 2026-04-16 at 11 13 09@2x

@bobrenjc93 bobrenjc93 changed the base branch from gh/bobrenjc93/41/base to main April 16, 2026 18:13
@bobrenjc93 bobrenjc93 merged commit 1c4e18b into main Apr 16, 2026
13 of 22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ciflow/8gpu CLA Signed This label is managed by the Meta Open Source bot.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants