@@ -267,6 +267,38 @@ def reset(self):
267267 self .per_param_grad_ready_counts = {}
268268 self .is_last_microbatch = True
269269
270+ def _post_param_sync (self ):
271+ """Run post-processing after param all-gather completes."""
272+ if self .ddp_config .reuse_grad_buf_for_mxfp8_param_ag :
273+ for bucket in self .buckets :
274+ is_bf16_weight_bucket = False
275+ for param in bucket .params :
276+ # Skip copying since bf16 weights in the mxfp8 model
277+ # are already mapped to param.data.
278+ if not is_float8tensor (param ):
279+ is_bf16_weight_bucket = True
280+ break
281+ param_start , param_end = bucket .param_to_index [param ]
282+ param_slice = bucket .param_data .view (- 1 )[param_start :param_end ]
283+ param .data .copy_ (param_slice .view (param .data .shape ))
284+ if is_bf16_weight_bucket :
285+ continue
286+ # All-gathered params are not needed after being copied to param.data.
287+ # Zero out the param buffer (shared with grad buffer) for gradient accumulation.
288+ # We cannot zero out the entire grad buffer because one grad buffer may
289+ # correspond to multiple param buffers. If we zero out the entire grad buffer,
290+ # it would clear the data of those param buffers that have not yet completed AG.
291+ bucket .param_data .zero_ ()
292+ return
293+
294+ quantized_params = []
295+ for bucket in self .buckets :
296+ for param in bucket .params :
297+ if is_float8tensor (param ) or is_nvfp4tensor (param ):
298+ quantized_params .append (param )
299+ if len (quantized_params ) > 0 :
300+ post_all_gather_processing (quantized_params )
301+
270302 def check_grads (self , check_for_nan_or_inf , check_for_large ):
271303 """
272304 Make sure norm of grads in bucket are not NaN prior to data-parallel
@@ -325,6 +357,7 @@ def start_param_sync(self, force_sync: bool = False):
325357 if self .param_gather_handle is not None :
326358 self .param_gather_handle .wait ()
327359 self .param_gather_handle = None
360+ self ._post_param_sync ()
328361 return
329362 else :
330363 assert self .param_gather_handle is None
@@ -344,6 +377,8 @@ def start_param_sync(self, force_sync: bool = False):
344377 dp_size = self .intra_distributed_optimizer_instance_size
345378 if dp_size == 1 :
346379 # Single-rank group (e.g., expt_dp_size == 1): no all-gather needed.
380+ if force_sync and self .ddp_config .overlap_param_gather :
381+ self ._post_param_sync ()
347382 self .param_gather_dispatched = True
348383 return
349384 local_rank = self .intra_distributed_optimizer_instance_rank
@@ -441,6 +476,8 @@ def start_param_sync(self, force_sync: bool = False):
441476 # (async_op=False) is used, `cm` is not None. Manually set to None for
442477 # consistency with prior code.
443478 self .param_gather_handle = None
479+ if force_sync and self .ddp_config .overlap_param_gather :
480+ self ._post_param_sync ()
444481 self .param_gather_dispatched = True
445482
446483 def finish_param_sync (self , skip_next_bucket_dispatch : bool = False ):
@@ -480,30 +517,7 @@ def finish_param_sync(self, skip_next_bucket_dispatch: bool = False):
480517 else :
481518 self .next_param_gather_bucket_group .start_param_sync ()
482519
483- # For the mxfp8_param with "reuse_grad_buf_for_mxfp8_param_ag=True",
484- # we need to copy the param_data from the shared_param/grad_buffer to param.data
485- # after the param all-gather.
486- if self .ddp_config .reuse_grad_buf_for_mxfp8_param_ag :
487- for bucket in self .buckets :
488- is_bf16_weight_bucket = False
489- for param in bucket .params :
490- # Skip copying since bf16 weights in the mxfp8 model
491- # are already mapped to param.data.
492- if not is_float8tensor (param ):
493- is_bf16_weight_bucket = True
494- break
495- param_start , param_end = bucket .param_to_index [param ]
496- param_slice = bucket .param_data .view (- 1 )[param_start :param_end ]
497- param .data .copy_ (param_slice .view (param .data .shape ))
498- if is_bf16_weight_bucket :
499- continue
500- # All-gathered params are not needed after being copied to param.data.
501- # Zero out the param buffer (shared with grad buffer) for gradient accumulation.
502- # We cannot zero out the entire grad buffer because one grad buffer may
503- # correspond to multiple param buffers. If we zero out the entire grad buffer,
504- # it would clear the data of those param buffers that have not yet completed AG.
505- bucket .param_data .zero_ ()
506- elif not self .ddp_config .use_distributed_optimizer :
520+ if not self .ddp_config .use_distributed_optimizer :
507521 for bucket in self .buckets :
508522 if bucket .layerwise_gather_list is None :
509523 continue
@@ -526,14 +540,7 @@ def finish_param_sync(self, skip_next_bucket_dispatch: bool = False):
526540 # (a view into grad_data) would start from the result of the
527541 # latest parameter all-gather instead of zero.
528542 bucket .grad_data .zero_ ()
529- else :
530- fp8_params = []
531- for bucket in self .buckets :
532- for param in bucket .params :
533- if is_float8tensor (param ):
534- fp8_params .append (param )
535- if len (fp8_params ) > 0 :
536- post_all_gather_processing (fp8_params )
543+ self ._post_param_sync ()
537544
538545 def start_grad_sync (self , force_all_reduce : Optional [bool ] = False ):
539546 """
0 commit comments