Skip to content

Commit 63e130f

Browse files
committed
[GraphTrainer] Support transformer_block_bucketing in aot_fx_trace mode
aot_fx_trace mode traces with record_module_stack=False, so nodes lack nn_module_stack metadata that manual_overlap_bucketing uses to match nodes to modules. This change uses the existing annotate_fn mechanism (already used for AC region tagging) to tag module FQNs during tracing, then provides a custom module_stack_fn to manual_overlap_bucketing. Changes: - Add annotate_module_fqns() and get_module_stack_from_annotation() to common_utils.py - Call annotate_module_fqns() in both llama3 and deepseek_v3 annotate functions - Add module_stack_fn parameter to transformer_block_bucketing_reordering_pass - Update construct_default_graph_passes to accept compile_config, model, and parallel_dims, and wire up the bucketing pass with reassign_to_pg - Pass config/model/parallel_dims from GraphTrainer to construct_default_graph_passes - Add numerics tests for both llama3 and deepseek_v3 with aot_fx_trace + transformer_block_bucketing
1 parent 878041c commit 63e130f

6 files changed

Lines changed: 125 additions & 6 deletions

File tree

torchtitan/experiments/graph_trainer/common_utils.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,31 @@
1818
from torchtitan.tools.logging import logger
1919

2020
_AC_REGION_ID = "ac_region_id"
21+
_IS_BWD = "is_bwd"
22+
_MODULE_FQN = "module_fqn"
23+
24+
25+
def _flatten_bucket_fqns(bucket_plans: list[list[str] | str]) -> list[str]:
26+
"""Flatten a possibly nested bucket plan list into a flat list of FQNs."""
27+
result = []
28+
for item in bucket_plans:
29+
if isinstance(item, list):
30+
result.extend(item)
31+
else:
32+
result.append(item)
33+
return result
34+
35+
36+
def annotate_module_fqns(model: nn.Module) -> None:
37+
"""Annotate bucket-level modules' forward with their fully-qualified names.
38+
39+
Uses get_transformer_block_buckets to determine which modules to annotate,
40+
ensuring the annotation granularity matches the bucketing granularity.
41+
"""
42+
fqns = _flatten_bucket_fqns(get_transformer_block_buckets(model))
43+
for fqn in fqns:
44+
submodule = model.get_submodule(fqn)
45+
submodule.forward = annotate_fn({_MODULE_FQN: fqn})(submodule.forward)
2146

2247

2348
def annotate_ac_regions(model: nn.Module) -> None:

torchtitan/experiments/graph_trainer/deepseek_v3/parallelize.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from torchtitan.distributed.tensor_parallel import maybe_enable_async_tp
2020
from torchtitan.experiments.graph_trainer.common_utils import (
2121
annotate_ac_regions,
22+
annotate_module_fqns,
2223
apply_graph_ac,
2324
)
2425
from torchtitan.experiments.graph_trainer.compile import apply_compile
@@ -55,6 +56,7 @@ def annotate_deepseekv3(model: GraphTrainerDeepSeekV3Model) -> None:
5556
)
5657
MoE.forward = annotate_fn({"EP": "compute"})(MoE.forward)
5758

59+
annotate_module_fqns(model)
5860
annotate_ac_regions(model)
5961

6062

torchtitan/experiments/graph_trainer/llama3/parallelize.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from torchtitan.distributed.tensor_parallel import maybe_enable_async_tp
1717
from torchtitan.experiments.graph_trainer.common_utils import (
1818
annotate_ac_regions,
19+
annotate_module_fqns,
1920
apply_graph_ac,
2021
)
2122
from torchtitan.experiments.graph_trainer.compile import apply_compile
@@ -32,10 +33,14 @@
3233
def annotate_llama(model: GraphTrainerLlama3Model) -> None:
3334
"""Attach annotations to FX graph nodes with ``torch.fx.traceback.annotate_fn``
3435
36+
- Module FQN annotation: Tags each submodule's forward with its
37+
fully-qualified name so that the transformer_block_bucketing pass
38+
can identify which graph nodes belong to which module.
3539
- AC region annotation: Tags each transformer block's forward with a unique
3640
ac_region_id so that apply_sac_pass can assign per-block ac_graph_id
3741
boundaries for the min-cut partitioner.
3842
"""
43+
annotate_module_fqns(model)
3944
annotate_ac_regions(model)
4045

4146

torchtitan/experiments/graph_trainer/make_fx_tracer.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -252,8 +252,11 @@ def _copy_fwd_metadata_to_bw_nodes(fx_g: torch.fx.GraphModule) -> None:
252252
Walks the graph in a single pass. The first node seen for each seq_nr is
253253
treated as the forward node.
254254
Subsequent nodes with the same seq_nr (typically backward nodes) receive
255-
the forward node's custom metadata.
255+
the forward node's custom metadata and are marked with ``is_bwd=True``
256+
in their ``custom`` dict.
256257
"""
258+
from torchtitan.experiments.graph_trainer.common_utils import _IS_BWD
259+
257260
seq_nr_to_fwd_node: dict[int, torch.fx.Node] = {}
258261

259262
for node in fx_g.graph.nodes:
@@ -268,6 +271,7 @@ def _copy_fwd_metadata_to_bw_nodes(fx_g: torch.fx.GraphModule) -> None:
268271
custom = fwd_node.meta.get("custom")
269272
if custom:
270273
node.meta.setdefault("custom", {}).update(custom)
274+
node.meta.setdefault("custom", {})[_IS_BWD] = True
271275
nn_module_stack = fwd_node.meta.get("nn_module_stack")
272276
if nn_module_stack is not None:
273277
node.meta["nn_module_stack"] = nn_module_stack.copy()
@@ -416,7 +420,7 @@ def fn_with_subclass_handling(*plain_args: Any) -> list:
416420
traced = make_fx(
417421
fn_with_subclass_handling,
418422
record_stack_traces=True,
419-
record_module_stack=False, # don't need nn_module_stack for now
423+
record_module_stack=True,
420424
)(*fake_args)
421425

422426
# Copy forward annotations to backward nodes.

torchtitan/experiments/graph_trainer/passes.py

Lines changed: 83 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,11 @@
3737
from torch.utils.checkpoint import CheckpointPolicy
3838

3939
from torchtitan.distributed.activation_checkpoint import _get_save_ops
40-
from torchtitan.experiments.graph_trainer.common_utils import _AC_REGION_ID
40+
from torchtitan.experiments.graph_trainer.common_utils import (
41+
_AC_REGION_ID,
42+
_IS_BWD,
43+
_MODULE_FQN,
44+
)
4145
from torchtitan.experiments.graph_trainer.make_fx_tracer import TracedResult
4246
from torchtitan.experiments.graph_trainer.reshard_after_forward import (
4347
annotate_fsdp_all_gather,
@@ -184,6 +188,7 @@ def remove_identity_slice_pass(
184188

185189
def construct_default_graph_passes(
186190
traced_result: "TracedResult",
191+
model: torch.nn.Module,
187192
) -> list[Callable]:
188193
"""Build the default pass list for the aot_fx_trace compile path.
189194
@@ -193,15 +198,24 @@ def construct_default_graph_passes(
193198
194199
Args:
195200
traced_result: The traced graph and metadata from ``trace_train_step``.
201+
model: The model for extracting transformer block buckets.
196202
197203
Returns:
198204
An ordered list of graph passes ready to apply.
199205
"""
206+
from torchtitan.experiments.graph_trainer.common_utils import (
207+
get_transformer_block_buckets,
208+
)
209+
200210
passes: list[Callable] = [
201211
functools.partial(tlparse_log_graph_pass, graph_name="make_fx_graph_traced"),
202212
remove_detach_pass,
203213
remove_identity_view_pass,
204214
remove_identity_slice_pass,
215+
functools.partial(
216+
joint_transformer_block_bucketing_reordering_pass,
217+
fsdp_manual_buckets=get_transformer_block_buckets(model),
218+
),
205219
]
206220

207221
# cudagraph should be the last pass.
@@ -252,18 +266,84 @@ def autobucketing_reordering_pass(
252266
return gm
253267

254268

269+
def _make_module_fqn_stack_fn(
270+
*, bwd: bool
271+
) -> Callable[[torch.fx.Node], list[tuple[str, type]]]:
272+
"""Create a module_stack_fn that filters nodes by forward/backward direction.
273+
274+
Args:
275+
bwd: If True, only return FQNs for backward nodes. If False, only
276+
return FQNs for forward nodes.
277+
278+
Returns:
279+
A callable compatible with make_graph_view's module_stack_fn parameter.
280+
"""
281+
282+
def _stack_fn(node: torch.fx.Node) -> list[tuple[str, type]]:
283+
custom = node.meta.get("custom", {})
284+
is_bwd = custom.get(_IS_BWD, False)
285+
if is_bwd != bwd:
286+
return []
287+
fqn = custom.get(_MODULE_FQN)
288+
if not fqn:
289+
return []
290+
return [(fqn, torch.nn.Module)]
291+
292+
return _stack_fn
293+
294+
255295
def transformer_block_bucketing_reordering_pass(
256296
gm: torch.fx.GraphModule,
257297
example_inputs: tuple | None = None,
258298
*,
259299
fsdp_manual_buckets,
260300
) -> torch.fx.GraphModule:
301+
"""Apply aten-level manual bucketing and reordering optimization.
302+
303+
Used by the AOT and JIT modes where forward and backward graphs are
304+
already separate.
261305
"""
262-
Apply aten-level manual bucketing and reordering optimization.
306+
manual_overlap_bucketing(
307+
gm,
308+
module_bucket_plans=fsdp_manual_buckets,
309+
insert_overlap_deps=False,
310+
module_stack_fn=_make_module_fqn_stack_fn(bwd=False),
311+
)
312+
gm.recompile()
313+
return gm
314+
315+
316+
def joint_transformer_block_bucketing_reordering_pass(
317+
gm: torch.fx.GraphModule,
318+
example_inputs: tuple | None = None,
319+
*,
320+
fsdp_manual_buckets,
321+
) -> torch.fx.GraphModule:
322+
"""Apply manual bucketing and reordering on a joint fwd+bwd graph.
323+
324+
The joint graph is processed in two passes to ensure each direction's
325+
collectives are bucketed independently and reordering uses the correct
326+
execution order (forward order for fwd, reversed order for bwd).
327+
328+
Used by the aot_fx_trace mode where forward and backward are in a
329+
single graph.
263330
"""
331+
# Forward pass: bucket and reorder in forward execution order
264332
manual_overlap_bucketing(
265-
gm, module_bucket_plans=fsdp_manual_buckets, insert_overlap_deps=False
333+
gm,
334+
module_bucket_plans=fsdp_manual_buckets,
335+
insert_overlap_deps=False,
336+
module_stack_fn=_make_module_fqn_stack_fn(bwd=False),
266337
)
338+
339+
# Backward pass: bucket and reorder backward collectives separately
340+
manual_overlap_bucketing(
341+
gm,
342+
module_bucket_plans=fsdp_manual_buckets,
343+
insert_overlap_deps=False,
344+
module_stack_fn=_make_module_fqn_stack_fn(bwd=True),
345+
)
346+
267347
gm.recompile()
268348
return gm
269349

torchtitan/experiments/graph_trainer/trainer.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,10 @@ def _make_fx_forward_backward_step(
128128
)
129129

130130
if self.config.compile.enable_passes:
131-
passes = construct_default_graph_passes(self._traced_step)
131+
passes = construct_default_graph_passes(
132+
self._traced_step,
133+
model=model,
134+
)
132135
self._traced_step.gm = apply_graph_passes(
133136
self._traced_step.gm,
134137
self._traced_step.example_inputs,

0 commit comments

Comments
 (0)