55import gc
66import logging
77import sys
8- from argparse import Namespace
98
109import torch
10+ import torch .nn .functional as F
11+
12+ from megatron .core .activations import situ_glu , squared_relu , tanh_soft_clamp
13+ from megatron .core .transformer .transformer_config import TransformerConfig
14+ from megatron .core .utils import log_single_rank
1115
1216logger = logging .getLogger (__name__ )
1317
1418
15- def _warmup_cross_entropy (args : Namespace , tp_group : torch .distributed .ProcessGroup ) -> None :
19+ def _warmup_cross_entropy (
20+ config : TransformerConfig ,
21+ tp_group : torch .distributed .ProcessGroup ,
22+ sequence : int ,
23+ micro_batch_size : int ,
24+ vocab : int ,
25+ logit_dtype : torch .dtype | None ,
26+ ) -> None :
1627 from megatron .core .fusions .fused_cross_entropy import fused_vocab_parallel_cross_entropy
1728
18- sequence = args .seq_length // args .context_parallel_size
19- vocab = args .padded_vocab_size
2029 if vocab <= 0 :
2130 raise ValueError ("CE vocabulary must be positive" )
2231 if vocab % tp_group .size ():
2332 raise ValueError ("CE vocabulary must be divisible by tensor parallel size" )
2433 for _ in range (2 ):
2534 logits = torch .zeros (
26- (sequence , args . micro_batch_size , vocab // tp_group .size ()),
35+ (sequence , micro_batch_size , vocab // tp_group .size ()),
2736 device = "cuda" ,
28- dtype = args . logit_dtype or args .params_dtype ,
37+ dtype = logit_dtype or config .params_dtype ,
2938 requires_grad = True ,
3039 )
3140 # Match the model's batch-major labels, including singleton-dimension
3241 # strides: contiguous() need not copy a transposed microbatch of one.
33- labels = torch .zeros ((args . micro_batch_size , sequence ), device = "cuda" , dtype = torch .long )
42+ labels = torch .zeros ((micro_batch_size , sequence ), device = "cuda" , dtype = torch .long )
3443 labels = labels .transpose (0 , 1 ).contiguous ()
3544 loss = fused_vocab_parallel_cross_entropy (logits , labels , tp_group )
3645 # Match LanguageModule.compute_language_model_loss, including the [b, s]
@@ -43,69 +52,108 @@ def _warmup_cross_entropy(args: Namespace, tp_group: torch.distributed.ProcessGr
4352 del gradient , loss_gradient , loss , labels , logits
4453
4554
46- def _warmup_activation (args : Namespace , tp_size : int , width : int , * , flatten : bool = False ) -> None :
55+ def _warmup_activation (
56+ config : TransformerConfig ,
57+ tp_size : int ,
58+ width : int ,
59+ sequence : int ,
60+ micro_batch_size : int ,
61+ * ,
62+ kernel : str ,
63+ flatten : bool = False ,
64+ ) -> None :
65+ """Warm one MLP activation path at the model's FC1 output shape.
66+
67+ ``kernel`` is what MLP.forward will run, selected by the caller from activation_func together
68+ with its gating and fusion settings; nothing here re-derives it from a single config field.
69+ "squared_relu", "swiglu", "geglu" and "gelu" are the fused kernels. "unfused" is the eager
70+ expression, whose jit-fused pieces (situ_glu, tanh_soft_clamp and activations such as
71+ squared_relu or quick_gelu) also compile on first use.
72+ """
73+ from megatron .core .fusions .fused_bias_geglu import bias_geglu_impl
4774 from megatron .core .fusions .fused_bias_gelu import bias_gelu_impl
4875 from megatron .core .fusions .fused_bias_swiglu import bias_swiglu_impl
4976 from megatron .core .fusions .fused_weighted_squared_relu import weighted_squared_relu_impl
5077
51- sequence = args .seq_length // args .context_parallel_size
52- leading_shape = (sequence , args .micro_batch_size )
78+ leading_shape = (sequence , micro_batch_size )
5379 if flatten :
54- leading_shape = (sequence * args .micro_batch_size ,)
80+ leading_shape = (sequence * micro_batch_size ,)
81+ # Gated activations consume FC1's [gate, linear] output, twice the width.
82+ gated = kernel in ("swiglu" , "geglu" ) or (kernel == "unfused" and config .gated_linear_unit )
83+ clamp = config .activation_func_tanh_clamp_scale
5584 for _ in range (2 ):
5685 x = torch .zeros (
57- (* leading_shape , width // tp_size * (2 if args . swiglu else 1 )),
86+ (* leading_shape , width // tp_size * (2 if gated else 1 )),
5887 device = "cuda" ,
59- dtype = args .params_dtype ,
88+ dtype = config .params_dtype ,
6089 requires_grad = True ,
6190 )
91+ # The unfused path adds its bias eagerly before the activation; nothing to warm there.
6292 bias = (
6393 torch .zeros (x .shape [- 1 ], device = "cuda" , dtype = x .dtype , requires_grad = True )
64- if args .add_bias_linear and not args . squared_relu
94+ if config .add_bias_linear and kernel not in ( " squared_relu" , "unfused" )
6595 else None
6696 )
67- if args .squared_relu :
68- output = weighted_squared_relu_impl (x , None , args .activation_func_tanh_clamp_scale )
69- elif args .swiglu :
97+ if kernel == "squared_relu" :
98+ output = weighted_squared_relu_impl (x , None , clamp )
99+ elif kernel == "unfused" :
100+ if gated and clamp is not None :
101+ linear_clamp = config .activation_func_tanh_clamp_scale_linear
102+ output = situ_glu (x , clamp , linear_clamp , config .glu_linear_offset )
103+ elif gated :
104+ x_glu , x_linear = torch .chunk (x , 2 , dim = - 1 )
105+ if (val := config .activation_func_clamp_value ) is not None :
106+ x_glu = x_glu .clamp (min = None , max = val )
107+ x_linear = x_linear .clamp (min = - val , max = val )
108+ output = config .activation_func (x_glu ) * (x_linear + config .glu_linear_offset )
109+ else :
110+ y = tanh_soft_clamp (x , clamp ) if clamp is not None else x
111+ output = config .activation_func (y )
112+ elif kernel == "swiglu" :
70113 output = bias_swiglu_impl (
71114 x ,
72115 bias ,
73- getattr ( args , " activation_func_fp8_input_store" , False ) ,
74- clamp_value = args .activation_func_clamp_value ,
75- gate_clamp_scale = args .activation_func_tanh_clamp_scale ,
76- linear_clamp_scale = args .activation_func_tanh_clamp_scale_linear ,
116+ config . activation_func_fp8_input_store ,
117+ clamp_value = config .activation_func_clamp_value ,
118+ gate_clamp_scale = config .activation_func_tanh_clamp_scale ,
119+ linear_clamp_scale = config .activation_func_tanh_clamp_scale_linear ,
77120 )
121+ elif kernel == "geglu" :
122+ output = bias_geglu_impl (x , bias )
78123 else :
79124 output = bias_gelu_impl (x , bias )
80125 inputs = (x , bias ) if bias is not None else (x ,)
81126 gradients = torch .autograd .grad (output , inputs , torch .ones_like (output ))
82127 del gradients , output , inputs , bias , x
83128
84129
85- def _warmup_residual (args : Namespace , tp_size : int ) -> None :
130+ def _warmup_residual (
131+ config : TransformerConfig , tp_size : int , sequence : int , micro_batch_size : int
132+ ) -> None :
86133 from megatron .core .fusions .fused_bias_dropout import bias_dropout_add_fused_train
87134
88- sequence = args .seq_length // args .context_parallel_size
89- if args .sequence_parallel :
135+ if config .sequence_parallel :
90136 sequence //= tp_size
91137 for grad_enabled in (False , True ):
92138 with torch .set_grad_enabled (grad_enabled ):
93139 for _ in range (2 ):
94140 x = torch .zeros (
95- (sequence , args . micro_batch_size , args .hidden_size ),
141+ (sequence , micro_batch_size , config .hidden_size ),
96142 device = "cuda" ,
97- dtype = args .params_dtype ,
143+ dtype = config .params_dtype ,
98144 requires_grad = True ,
99145 )
100146 residual = torch .zeros_like (
101147 x ,
102- dtype = torch .float32 if args .fp32_residual_connection else x .dtype ,
148+ dtype = torch .float32 if config .fp32_residual_connection else x .dtype ,
103149 requires_grad = True ,
104150 )
105151 bias = (
106- torch .zeros_like (x [0 , 0 ], requires_grad = True ) if args .add_bias_linear else None
152+ torch .zeros_like (x [0 , 0 ], requires_grad = True )
153+ if config .add_bias_linear
154+ else None
107155 )
108- output = bias_dropout_add_fused_train ((x , bias ), residual , args .hidden_dropout )
156+ output = bias_dropout_add_fused_train ((x , bias ), residual , config .hidden_dropout )
109157 if grad_enabled :
110158 inputs = (x , residual , bias ) if bias is not None else (x , residual )
111159 gradients = torch .autograd .grad (output , inputs , torch .ones_like (output ))
@@ -125,7 +173,82 @@ def _shutdown_compile_workers() -> None:
125173 logger .warning ("This PyTorch build lacks compiler-worker shutdown" )
126174
127175
128- def warmup_training_kernels (args : Namespace , tp_group : torch .distributed .ProcessGroup ) -> None :
176+ def warmup_training_kernels (
177+ model_config ,
178+ tp_group : torch .distributed .ProcessGroup ,
179+ * ,
180+ seq_length : int ,
181+ micro_batch_size : int ,
182+ ) -> None :
183+ """Warm up training kernels for a GPT or Hybrid model config at the training batch shape.
184+
185+ Entry points that still pass a provider function instead of a model config leave
186+ ``model_config`` unset; there is no TransformerConfig to select kernels from, so nothing
187+ is warmed.
188+ """
189+ from megatron .training .models import GPTModelConfig , HybridModelConfig
190+ from megatron .training .vocab_utils import calculate_padded_vocab_size
191+
192+ if not isinstance (model_config , (GPTModelConfig , HybridModelConfig )):
193+ log_single_rank (
194+ logger , logging .INFO , "Skipping training-kernel warmup: no GPT/Hybrid model config"
195+ )
196+ return
197+ config = model_config .transformer
198+ vocab_size = model_config .vocab_size
199+ if vocab_size is not None and model_config .should_pad_vocab :
200+ vocab_size = calculate_padded_vocab_size (
201+ vocab_size ,
202+ model_config .make_vocab_size_divisible_by ,
203+ config .tensor_model_parallel_size ,
204+ logging_enabled = False ,
205+ )
206+ _warmup_training_kernels (
207+ config ,
208+ tp_group ,
209+ seq_length = seq_length ,
210+ micro_batch_size = micro_batch_size ,
211+ padded_vocab_size = vocab_size ,
212+ logit_dtype = model_config .logit_dtype ,
213+ )
214+
215+
216+ def _select_activation_kernel (config : TransformerConfig ) -> str | None :
217+ """Return the activation path MLP.forward runs for fixed-shape (dense and shared) MLPs.
218+
219+ None means nothing to warm: TE activations, or fused combinations MLP.forward rejects.
220+ Quick-GeLU and the per-token-weighted kernels are fused only for routed experts, whose
221+ token counts are data-dependent, so they are left to runtime compilation.
222+ """
223+ if config .use_te_activation_func :
224+ return None
225+ if (
226+ config .activation_func == squared_relu
227+ and not config .gated_linear_unit
228+ and config .use_fused_weighted_squared_relu
229+ and config .activation_func_tanh_clamp_scale is not None
230+ ):
231+ return "squared_relu"
232+ if not config .bias_activation_fusion :
233+ return "unfused"
234+ if config .activation_func == F .silu and config .gated_linear_unit :
235+ return "swiglu"
236+ if config .activation_func == F .gelu :
237+ if config .gated_linear_unit :
238+ return "geglu"
239+ return "gelu" if config .add_bias_linear else None
240+ return None
241+
242+
243+ def _warmup_training_kernels (
244+ config : TransformerConfig ,
245+ tp_group : torch .distributed .ProcessGroup ,
246+ * ,
247+ seq_length : int ,
248+ micro_batch_size : int ,
249+ padded_vocab_size : int | None ,
250+ logit_dtype : torch .dtype | None = None ,
251+ ) -> None :
129252 """Compile/autotune supported kernels, then release temporary startup resources.
130253
131254 Call after set_jit_fusion_options() on every tensor-parallel rank, before
@@ -134,42 +257,52 @@ def warmup_training_kernels(args: Namespace, tp_group: torch.distributed.Process
134257 shapes; no training data or parameters are needed. RNG state and compiler
135258 parallelism are preserved. Dynamic expert shapes and library-specific kernels
136259 can still compile later; shutting down workers does not disable compilation.
260+
261+ Args:
262+ config: The TransformerConfig used to build the model.
263+ tp_group: The model's tensor-parallel process group.
264+ seq_length: Actual training sequence length before context parallel partitioning.
265+ micro_batch_size: Microbatch size used by the training schedule and dataloader.
266+ padded_vocab_size: Global padded vocabulary size before tensor parallel partitioning.
267+ May be None when native fused cross-entropy is disabled.
268+ logit_dtype: Model output-projection dtype; None uses config.params_dtype for warmup.
137269 """
138- if args .seq_length // args .context_parallel_size <= 0 or args .micro_batch_size <= 0 :
270+ sequence = seq_length // config .context_parallel_size
271+ if sequence <= 0 or micro_batch_size is None or micro_batch_size <= 0 :
139272 raise ValueError ("Kernel warmup dimensions must be positive" )
140273 tp_size = tp_group .size ()
141274 # New warmups (notably dropout) must not advance training RNG state. The
142275 # existing JIT warmup runs separately and retains its original RNG behavior.
143276 with torch .random .fork_rng (devices = [torch .cuda .current_device ()]), torch .enable_grad ():
144- if args .cross_entropy_loss_fusion and args .cross_entropy_fusion_impl == "native" :
145- _warmup_cross_entropy (args , tp_group )
277+ if config .cross_entropy_loss_fusion and config .cross_entropy_fusion_impl == "native" :
278+ if padded_vocab_size is None :
279+ raise ValueError ("Model vocabulary must be resolved before kernel warmup" )
280+ _warmup_cross_entropy (
281+ config , tp_group , sequence , micro_batch_size , padded_vocab_size , logit_dtype
282+ )
146283
147- squared_relu = (
148- args .squared_relu
149- and args .use_fused_weighted_squared_relu
150- and args .activation_func_tanh_clamp_scale is not None
151- )
152- swiglu = args .swiglu and args .bias_swiglu_fusion
153- gelu = (
154- not args .squared_relu
155- and not args .swiglu
156- and not args .quick_geglu
157- and args .bias_gelu_fusion
158- and args .add_bias_linear
159- )
160- if not args .use_te_activation_func and (squared_relu or swiglu or gelu ):
284+ kernel = _select_activation_kernel (config )
285+ if kernel is not None :
161286 # Shared and dense MLPs have fixed shapes. Routed-expert token counts
162287 # are data-dependent and are intentionally left to runtime compilation.
163- widths = {args .ffn_hidden_size }
164- if args .moe_shared_expert_intermediate_size :
165- widths .add (args .moe_shared_expert_intermediate_size )
288+ widths = {config .ffn_hidden_size }
289+ if config .moe_shared_expert_intermediate_size :
290+ widths .add (config .moe_shared_expert_intermediate_size )
166291 for width in sorted (widths ):
167- _warmup_activation (args , tp_size , width )
168292 # MLP paths also use flattened [tokens, width] activations;
169293 # Dynamo specializes on tensor rank even with identical numel.
170- _warmup_activation (args , tp_size , width , flatten = True )
171- if args .bias_dropout_fusion :
172- _warmup_residual (args , tp_size )
294+ for flatten in (False , True ):
295+ _warmup_activation (
296+ config ,
297+ tp_size ,
298+ width ,
299+ sequence ,
300+ micro_batch_size ,
301+ kernel = kernel ,
302+ flatten = flatten ,
303+ )
304+ if config .bias_dropout_fusion :
305+ _warmup_residual (config , tp_size , sequence , micro_batch_size )
173306
174307 # Each helper has returned after invoking its kernels and waiting for their
175308 # compilation. Finish GPU work before releasing unused pinned blocks.
0 commit comments