@@ -33,6 +33,15 @@ def iter_params(module: Module, recurse=False) -> Iterable[Parameter]:
3333 return map (lambda pair : pair [1 ], get_all_parameters (module , recurse ))
3434
3535
36+ class TraceMode (Enum ):
37+ # Record trace of the network during a single forward+backward (for training) or forward (for inference)
38+ RECORD = 1
39+ # Use recorded network trace to optimize current forward+backward or forward
40+ COMPLETE = 2
41+ # Recorded trace does not match current forward+backward or forward pass.
42+ INVALID = 3
43+
44+
3645class PartitionedParameterCoordinator :
3746 """Handles partitioning and gathering of parameters."""
3847 class __InflightParamRegistry (UserDict ):
@@ -65,9 +74,8 @@ def __init__(
6574 self .__inflight_param_registry = __class__ .__InflightParamRegistry ()
6675 # keeps track of the number of submodules invoked so far.
6776 self .__step_id : int = 0
68- # whether or not we have completed a trace of the entire network. This should
69- # always be true after the first forward pass + backward pass.
70- self .trace_complete : bool = False
77+ # network tracing mode
78+ self .__trace_mode : TraceMode = TraceMode .RECORD
7179 # sequence of submodules/parameters in forward pass + backward pass
7280 self .__submodule_order : Iterable [Module ] = []
7381 self .__param_order : Iterable [__class__ .__ParamInTrace ] = []
@@ -110,13 +118,46 @@ def __init__(
110118 Bookkeeping operations used to track where we are in the forward/backward pass
111119 """
112120
113- def record_trace (self , sub_module : Module ) -> None :
121+ def _clear_trace_structures (self ) -> None :
122+ self .__submodule_order = []
123+ self .__param_order = []
124+ self .__most_recent_step_id_param_fetched_for = collections .defaultdict (
125+ lambda : int (- 1e10 ))
126+ self .__param_queue = None
127+
128+ def is_complete_trace (self ) -> bool :
129+ return self .__trace_mode == TraceMode .COMPLETE
130+
131+ def is_invalid_trace (self ) -> bool :
132+ return self .__trace_mode == TraceMode .INVALID
133+
134+ def is_record_trace (self ) -> bool :
135+ return self .__trace_mode == TraceMode .RECORD
136+
137+ def _invalidate_trace (self ) -> None :
138+ if self .is_invalid_trace ():
139+ raise RuntimeError ("attempted to invalidate already invalid trace" )
140+ self .__trace_mode = TraceMode .INVALID
141+ self ._clear_trace_structures ()
142+
143+ def trace_prologue (self , sub_module : Module ) -> None :
144+ if self .is_complete_trace ():
145+ # sub_module must match expectation else invalidate trace cache
146+ if sub_module != self .__submodule_order [self .__step_id ]:
147+ self ._invalidate_trace ()
148+
149+ def record_module (self , sub_module : Module ) -> None :
114150 """adds sub module to trace"""
115- if self .trace_complete :
151+ if not self .is_record_trace () :
116152 raise RuntimeError (
117- "attempted to record trace when trace was already complete" )
118-
153+ f"attempted to record trace when status = { self .__trace_mode } " )
119154 self .__submodule_order .append (sub_module )
155+
156+ def record_parameters (self , sub_module : Module ) -> None :
157+ """adds sub module to trace"""
158+ if not self .is_record_trace ():
159+ raise RuntimeError (
160+ f"attempted to record trace when status = { self .__trace_mode } " )
120161 for param in sorted (set (iter_params (sub_module )), key = lambda p : p .ds_id ):
121162 self .__param_order .append (
122163 __class__ .__ParamInTrace (param = param ,
@@ -129,19 +170,25 @@ def reset_step(self) -> None:
129170 f"still have inflight params "
130171 f"{ [p .ds_summary for p in self .__inflight_param_registry .keys ()]} " )
131172
132- if not self .trace_complete :
133- # make sure that recorded parameter and submodule orders are
173+ if not self .is_complete_trace (): # not self. trace_complete:
174+ # Make sure that recorded parameter and submodule orders are
134175 # identical across ranks
135176 assert_ints_same_as_other_ranks ([m .id for m in self .__submodule_order ])
136177 assert_ints_same_as_other_ranks ([p .param .ds_id for p in self .__param_order ])
137178 assert_ints_same_as_other_ranks (
138179 [p .step_id_last_used_at for p in self .__param_order ])
139180
140- self .__submodule_order = tuple (self .__submodule_order ) # freeze
141- self .__param_order = tuple (self .__param_order ) # freeze
142- self .trace_complete = True
143- print_rank_0 (f"completed trace: { [m .id for m in self .__submodule_order ]} " ,
144- force = False )
181+ if self .is_record_trace ():
182+ # Successfully recorded a trace
183+ self .__submodule_order = tuple (self .__submodule_order ) # freeze
184+ self .__param_order = tuple (self .__param_order ) # freeze
185+ self .__trace_mode = TraceMode .COMPLETE # self.trace_complete = True
186+ print_rank_0 (
187+ f"completed trace: { [m .id for m in self .__submodule_order ]} " ,
188+ force = False )
189+ else :
190+ # Enable trace recording for next forward/backward pass
191+ self .__trace_mode = TraceMode .RECORD
145192
146193 self .__param_queue = collections .deque (self .__param_order ) # reset fetch queue
147194 self .__most_recent_step_id_param_fetched_for = collections .defaultdict (
@@ -199,9 +246,8 @@ def fetch_sub_module(self, current_submodule: Module) -> None:
199246 torch .cuda .current_stream ().wait_stream (self .__allgather_stream )
200247
201248 # kick off parameter prefetches for upcoming modules
202- # don't prefetch if we dont have a completed model trace, or if we aren't
203- # training (throws off the tracing and don't want to prefetch modules for bwd)
204- if self .trace_complete and current_submodule .training :
249+ # don't prefetch if we dont have a completed model trace
250+ if self .is_complete_trace ():
205251 # go through the parameters we need for the current module and pop them
206252 # off the fetch queue so that they aren't prefetched later.
207253 # if params have already been popped off the fetch queue by earlier
@@ -228,24 +274,26 @@ def fetch_sub_module(self, current_submodule: Module) -> None:
228274 )
229275
230276 # kick off all gather for params in the next few submodules (prefetch)
231- max_params_to_prefetch = min (
232- self .__max_n_available_params - self .__n_available_params ,
233- self .__prefetch_bucket_sz )
234- params_to_prefetch = set ()
235- numel_prefetching = 0
236- while self .__param_queue and numel_prefetching < max_params_to_prefetch :
237- param_in_trace : __class__ .__ParamInTrace = self .__param_queue .popleft ()
238- self .__most_recent_step_id_param_fetched_for [
239- param_in_trace .param ] = param_in_trace .step_id_last_used_at
240- if param_in_trace .param not in params_to_prefetch :
241- params_to_prefetch .add (param_in_trace .param )
242- numel_prefetching += param_in_trace .param .ds_numel
243- for param in params_to_prefetch :
244- debug_rank0 (f"-prefetch: { param .ds_summary ()} " )
245- self .__all_gather_params (params_to_prefetch )
246-
247- if self .__prefetch_nvme :
248- self .__prefetch_nvme_param_partitions ()
277+ if self .__prefetch_bucket_sz > 0 :
278+ max_params_to_prefetch = min (
279+ self .__max_n_available_params - self .__n_available_params ,
280+ self .__prefetch_bucket_sz )
281+ params_to_prefetch = set ()
282+ numel_prefetching = 0
283+ while self .__param_queue and numel_prefetching < max_params_to_prefetch :
284+ param_in_trace : __class__ .__ParamInTrace = self .__param_queue .popleft (
285+ )
286+ self .__most_recent_step_id_param_fetched_for [
287+ param_in_trace .param ] = param_in_trace .step_id_last_used_at
288+ if param_in_trace .param not in params_to_prefetch :
289+ params_to_prefetch .add (param_in_trace .param )
290+ numel_prefetching += param_in_trace .param .ds_numel
291+ for param in params_to_prefetch :
292+ debug_rank0 (f"-prefetch: { param .ds_summary ()} " )
293+ self .__all_gather_params (params_to_prefetch )
294+
295+ if self .__prefetch_nvme :
296+ self .__prefetch_nvme_param_partitions ()
249297
250298 self .__step_id += 1
251299
@@ -256,9 +304,8 @@ def release_sub_module(self, submodule: Module) -> None:
256304 be released."""
257305 params_to_release = (self .__params_to_release (submodule ,
258306 self .__step_id )
259- if self .trace_complete else set (
307+ if self .is_complete_trace () else set (
260308 p .ds_id for p in iter_params (submodule )))
261-
262309 for param in iter_params (submodule ):
263310 param .ds_active_sub_modules .discard (submodule .id )
264311 if param .ds_id in params_to_release and not param .is_external_param :
@@ -311,7 +358,7 @@ def __release_param(self, param: Parameter) -> None:
311358 def __params_to_release (self ,
312359 submodule_to_release : Module ,
313360 step_id : int ) -> Set [int ]:
314- if not self .trace_complete :
361+ if not self .is_complete_trace () :
315362 raise RuntimeError ("expected trace to be complete" )
316363
317364 params_to_release = set (p .ds_id for p in iter_params (submodule_to_release )
@@ -335,7 +382,7 @@ def __prefetch_nvme_param_partitions(self) -> None:
335382 """swap in parameter partitions from nvme for those parameters that will be used
336383 after the ones that are already being prefetched into full parameters
337384 """
338- if not self .trace_complete :
385+ if not self .is_complete_trace () :
339386 return
340387
341388 numel_in_flight = sum (param .ds_numel for param in self .__inflight_param_registry )
0 commit comments