@@ -82,6 +82,9 @@ def construct_default_graph_passes(
8282 regional_inductor_pass ,
8383 ]
8484
85+ # Insert kernel annotation markers before cudagraph capture.
86+ passes .append (insert_kernel_annotations_pass )
87+
8588 # cudagraph should be the last pass.
8689 from torchtitan .experiments .graph_trainer .cudagraph import is_cudagraph_compatible
8790
@@ -260,6 +263,74 @@ def _get_fake_mode_from_gm(gm: torch.fx.GraphModule):
260263 return gm
261264
262265
266+ def _mark_kernels_enter (annotation : dict ) -> object :
267+ """Helper called from FX graph to enter a mark_kernels scope."""
268+ from torch .cuda ._graph_annotations import mark_kernels
269+
270+ ctx = mark_kernels (annotation )
271+ ctx .__enter__ ()
272+ return ctx
273+
274+
275+ def _mark_kernels_exit (ctx : object ) -> None :
276+ """Helper called from FX graph to exit a mark_kernels scope."""
277+ ctx .__exit__ (None , None , None ) # type: ignore[union-attr]
278+
279+
280+ def insert_kernel_annotations_pass (
281+ gm : torch .fx .GraphModule ,
282+ example_inputs : tuple | None = None ,
283+ ) -> torch .fx .GraphModule :
284+ """Insert mark_kernels() calls at component boundaries in the FX graph.
285+
286+ Reads ``node.meta["custom"]["component"]`` (set via
287+ ``torch.fx.traceback.annotate``) and inserts enter/exit calls so that
288+ CUDA graph capture records the annotations.
289+ """
290+ graph = gm .graph
291+ current_component : str | None = None
292+ current_ctx_node = None
293+
294+ for node in list (graph .nodes ):
295+ component = (node .meta .get ("custom" ) or {}).get ("component" )
296+
297+ if component != current_component :
298+ # Close previous scope
299+ if current_ctx_node is not None :
300+ with graph .inserting_before (node ):
301+ exit_node = graph .call_function (
302+ _mark_kernels_exit , (current_ctx_node ,)
303+ )
304+ exit_node .meta ["custom" ] = {}
305+ current_ctx_node = None
306+
307+ # Open new scope
308+ if component is not None :
309+ with graph .inserting_before (node ):
310+ enter_node = graph .call_function (
311+ _mark_kernels_enter ,
312+ ({"component" : component },),
313+ )
314+ enter_node .meta ["custom" ] = {}
315+ current_ctx_node = enter_node
316+
317+ current_component = component
318+
319+ # Close any trailing scope (before output/return)
320+ if current_ctx_node is not None :
321+ output_nodes = [n for n in graph .nodes if n .op == "output" ]
322+ if output_nodes :
323+ with graph .inserting_before (output_nodes [0 ]):
324+ exit_node = graph .call_function (
325+ _mark_kernels_exit , (current_ctx_node ,)
326+ )
327+ exit_node .meta ["custom" ] = {}
328+
329+ graph .lint ()
330+ gm .recompile ()
331+ return gm
332+
333+
263334def cudagraph_pass (
264335 gm : torch .fx .GraphModule ,
265336 example_inputs : tuple ,
@@ -680,6 +751,7 @@ def tlparse_log_graph_pass(
680751 "auto_bucketing" : autobucketing_reordering_pass ,
681752 "transformer_block_bucketing" : transformer_block_bucketing_reordering_pass ,
682753 "regional_inductor" : regional_inductor_pass ,
754+ "insert_kernel_annotations" : insert_kernel_annotations_pass ,
683755 "cudagraph" : cudagraph_pass ,
684756 "full_inductor_compilation" : full_inductor_compilation_pass ,
685757}
0 commit comments