@@ -77,6 +77,22 @@ def clear(self):
7777fwd_real_inputs = []
7878
7979
80+ def cleanup_compiled_backward_state (frame_id = None , owned_frames = None ):
81+ """Release engine-owned process-global compiled-backward state."""
82+ if frame_id is None :
83+ if owned_frames is None :
84+ frames_needing_bwd .clear ()
85+ else :
86+ frames_needing_bwd .difference_update (owned_frames )
87+ owned_frames .clear ()
88+ else :
89+ frames_needing_bwd .discard (frame_id )
90+ if owned_frames is not None :
91+ owned_frames .discard (frame_id )
92+ if len (frames_needing_bwd ) == 0 :
93+ unpatch_compiled_func ()
94+
95+
8096def register_compile_pass (name : str , opt_pass_fn , contract = None ):
8197 from .passes .contract import register_pass_contract
8298 opt_passes [name ] = opt_pass_fn
@@ -97,7 +113,8 @@ def init_schedule(schedule):
97113 remaining_schedule = deque (schedule )
98114
99115
100- def launch_compile_passes (global_steps : int ):
116+ def launch_compile_passes (global_steps : int , owned_frames = None ):
117+ """Advance the pass schedule and discard state owned by the previous compile cycle."""
101118 global next_pass_step , next_passes
102119
103120 if len (remaining_schedule ) > 0 and global_steps == remaining_schedule [0 ][0 ]:
@@ -109,6 +126,7 @@ def launch_compile_passes(global_steps: int):
109126 graph_order_with_frame_id .clear ()
110127 profiling_results .clear ()
111128 param_manager .clear ()
129+ cleanup_compiled_backward_state (owned_frames = owned_frames )
112130 frames_partitioned .clear ()
113131
114132
@@ -201,6 +219,21 @@ def set_example_values_to_symints(real_inputs, param_indices=None):
201219 return tuple (real_inputs_ret )
202220
203221
222+ def _get_fw_real_inputs (local_real_inputs , input_storage : InputStorage , graph_id : int , debug_log : bool = False ):
223+ """Resolve graph-local real inputs from the one-shot queue or persistent storage."""
224+ if local_real_inputs :
225+ return local_real_inputs .popleft ()
226+
227+ if input_storage .has_data ():
228+ if debug_log :
229+ log_rank0 (f"Retrieving real inputs from storage for graph_id={ graph_id } " , enable = True )
230+ return input_storage .get ()
231+
232+ raise RuntimeError (f"No real inputs available for graph_id { graph_id } . "
233+ f"Local queue size: { len (local_real_inputs )} , "
234+ f"storage has data: { input_storage .has_data ()} " )
235+
236+
204237def run_opt_passes (opt_passes : List [Callable ],
205238 gm : GraphModule ,
206239 graph_id : int ,
@@ -243,14 +276,18 @@ def run_opt_passes(opt_passes: List[Callable],
243276 get_accelerator ().empty_cache ()
244277
245278
246- def make_backend (backend , compile_config , compile_kwargs = {}):
279+ def make_backend (backend , compile_config , compile_kwargs = {}, owned_frames = None ):
247280
248281 register_custom_ops ()
249282
250283 # Extract values from compile_config
251284 debug_log = compile_config .debug_log
252285 free_activation = compile_config .free_activation and not is_backend_inductor (backend )
253286
287+ if owned_frames is None :
288+ owned_frames = set ()
289+ owner_token = object ()
290+
254291 def backend_fn (gm : GraphModule , real_inputs ):
255292 graph_id = id (gm .graph )
256293
@@ -263,6 +300,7 @@ def backend_fn(gm: GraphModule, real_inputs):
263300 # This check cannot be placed here because autograd creates the fw/bw compiler callables before graph
264301 # partitioning. It is thus postponed to the point where the fw compiler is called.
265302 frame_id = gm .meta ["dynamo_compile_id" ].frame_id
303+ frame_key = (owner_token , frame_id )
266304 graph_order_with_frame_id .add_graph (graph_id , frame_id )
267305
268306 z3_partition = any (hasattr (v , "ds_id" ) for v in real_inputs )
@@ -275,17 +313,15 @@ def backend_fn(gm: GraphModule, real_inputs):
275313 param_indices = [(i , input_val .param_id , input_val .shape ) for i , input_val in enumerate (real_inputs )
276314 if isinstance (input_val , torch .nn .Parameter )]
277315
278- global fwd_real_inputs
279-
280316 # Create an InputStorage instance for this specific graph
281317 # It will be captured by the make_fw_graph closure, eliminating the need for graph ID management
282318 input_storage = InputStorage (keep_int_input_tensors = compile_config .keep_int_input_tensors ,
283319 keep_all_input_tensors = compile_config .keep_all_input_tensors )
284320
285- # Store in both list (for backward compatibility) and storage (for persistence)
321+ # Store in a closure-local queue and storage (for persistence).
286322 # The input_storage keeps tensor metadata to handle cases where
287323 # backend_fn is called once but make_fw_graph is called multiple times
288- fwd_real_inputs . append ( real_inputs )
324+ local_fwd_real_inputs = deque ([ real_inputs ] )
289325 input_storage .put (real_inputs )
290326
291327 global profiling_results
@@ -304,20 +340,10 @@ def make_fw_graph(gm, sample_inputs):
304340 if needs_backward :
305341 if len (frames_needing_bwd ) == 0 :
306342 patch_compiled_func ()
307- frames_needing_bwd .add (frame_id )
308-
309- # Try to get real_inputs from the list first, then from storage
310- if fwd_real_inputs :
311- real_inputs = fwd_real_inputs .pop (0 )
312- elif input_storage .has_data ():
313- # Note: input_storage is captured from the enclosing backend_fn scope
314- # Materialize tensors from storage when list is empty
315- log_rank0 (f"Retrieving real inputs from storage for graph_id={ graph_id } " , enable = debug_log )
316- real_inputs = input_storage .get ()
317- else :
318- raise RuntimeError (f"No real inputs available for graph_id { graph_id } . "
319- f"List size: { len (fwd_real_inputs )} , Storage has data: { input_storage .has_data ()} " )
343+ frames_needing_bwd .add (frame_key )
344+ owned_frames .add (frame_key )
320345
346+ real_inputs = _get_fw_real_inputs (local_fwd_real_inputs , input_storage , graph_id , debug_log = debug_log )
321347 real_inputs = set_example_values_to_symints (real_inputs )
322348
323349 param_manager [graph_id ] = DSGraphParamManager (gm .graph , real_inputs , param_indices )
@@ -385,9 +411,7 @@ def make_bw_graph(gm, sample_inputs):
385411 add_free_activations (graph_id , gm .graph ,
386412 get_activation_node_names (gm .graph , param_nodes_bw , non_param_input_names ))
387413
388- frames_needing_bwd .remove (frame_id )
389- if len (frames_needing_bwd ) == 0 :
390- unpatch_compiled_func ()
414+ cleanup_compiled_backward_state (frame_key , owned_frames )
391415
392416 log_rank0 (
393417 f"Bwd end { graph_index } graph_id={ graph_id } alloc_mem={ get_accelerator ().memory_allocated ()} graph={ gm .graph } " ,
@@ -415,10 +439,15 @@ def compiler_fn(gm, sample_inputs):
415439 partition_fn = partition_fn )
416440 return torch ._dynamo .optimize (** compile_kwargs )(aot_mod )
417441 elif backend == "inductor" :
418- patch_create_aot_dispatcher_function (graph_id , z3_partition , make_fw_graph , make_bw_graph , real_inputs ,
419- param_indices , param_manager , frame_id , frames_partitioned )
420-
421- return torch ._inductor .compile (gm , real_inputs )
442+ restore_aotautograd = patch_create_aot_dispatcher_function (graph_id , z3_partition , make_fw_graph ,
443+ make_bw_graph , real_inputs , param_indices ,
444+ param_manager , frame_id , frames_partitioned )
445+ try :
446+ return torch ._inductor .compile (gm , real_inputs )
447+ finally :
448+ # AotAutograd.__init__ is process-global; never leak this
449+ # graph-specific compiler wiring into a later compilation.
450+ restore_aotautograd ()
422451
423452 raise ValueError (f"Unsupported backend { backend } " )
424453
0 commit comments