@@ -90,10 +90,9 @@ def _pad_seq_tensor(t: Optional[Tensor], target_len: int) -> Optional[Tensor]:
9090def _pad_cu_seqlens (cu_seqlens : Optional [Tensor ], target_entries : int ) -> Optional [Tensor ]:
9191 """Pad a cu_seqlens tensor to exactly ``target_entries`` entries.
9292
93- Asserts the actual entry count does not exceed ``target_entries``: this is
94- the reviewer-flagged overflow case and corresponds to "too many packed
95- sequences in this microbatch for thd_max_num_seqs". Failing fast prevents
96- a silent CUDA-graph shape mismatch at replay.
93+ Asserts the actual entry count does not exceed ``target_entries``. An
94+ oversized pack cannot be represented by the configured static cu_seqlens
95+ buffer and would not match captured CUDA Graph replay shapes.
9796 """
9897 if cu_seqlens is None :
9998 return None
@@ -114,33 +113,21 @@ def _pad_cu_seqlens(cu_seqlens: Optional[Tensor], target_entries: int) -> Option
114113 return padded
115114
116115
117- def pad_thd_for_cuda_graph (
116+ def _round_up_to_alignment (value : int , alignment : int ) -> int :
117+ assert alignment > 0 , f"Packed sequence padding alignment must be > 0, got { alignment } ."
118+ return ((value + alignment - 1 ) // alignment ) * alignment
119+
120+
121+ def _resolve_thd_padding_lengths (
118122 tokens : Optional [Tensor ],
119123 labels : Optional [Tensor ],
120124 loss_mask : Optional [Tensor ],
121125 position_ids : Optional [Tensor ],
122126 packed_seq_params : PackedSeqParams ,
123- max_seqlen : int ,
124- max_num_seqs : int ,
125- ) -> Tuple [
126- Optional [Tensor ],
127- Optional [Tensor ],
128- Optional [Tensor ],
129- Optional [Tensor ],
130- PackedSeqParams ,
131- Optional [Tensor ],
132- ]:
133- """Pad THD batch data to fixed sizes for CUDA Graph compatibility.
134-
135- CUDA Graph requires static tensor shapes. This function pads:
136- - tokens, labels, loss_mask, position_ids along dim=-1 to max_seqlen
137- - cu_seqlens tensors to (max_num_seqs + 1) entries, filled with actual_T
138- - Generates padding_mask for MoE aux loss exclusion
139-
140- Returns:
141- Padded (tokens, labels, loss_mask, position_ids, packed_seq_params, padding_mask)
142- padding_mask: [1, max_seqlen] bool tensor, True at padding positions.
143- """
127+ target_len : Optional [int ],
128+ alignment : Optional [int ],
129+ ) -> Tuple [int , int , int , int , torch .device , bool ]:
130+ """Resolve local/global THD padding lengths without changing tensors."""
144131
145132 actual_T = None
146133 mask_device = None
@@ -166,48 +153,23 @@ def pad_thd_for_cuda_graph(
166153 else parallel_state .get_context_parallel_world_size ()
167154 )
168155 cp_rank = parallel_state .get_context_parallel_rank () if cp_size > 1 else 0
169- max_seqlen_global = max_seqlen * cp_size
170-
171- if actual_T is not None and packed_seq_params .cu_seqlens_q is not None :
172- _cu = packed_seq_params .cu_seqlens_q
173- _individual_lens = _cu [1 :] - _cu [:- 1 ]
174- _max_individual = int (_individual_lens .max ().item ()) if _individual_lens .numel () > 0 else 0
175- assert _max_individual <= max_seqlen_global , (
176- f"Individual request length ({ _max_individual } ) exceeds the global max sequence length "
177- f"({ max_seqlen_global } = max_seqlen_per_dp_cp_rank { max_seqlen } * cp_size { cp_size } ). "
178- f"Each request must fit within the CUDA Graph static buffer after CP partitioning. "
179- f"Increase --max-seqlen-per-dp-cp-rank or --seq-length, or filter out overlong "
180- f"requests."
181- )
182-
183- tokens = _pad_seq_tensor (tokens , max_seqlen )
184- labels = _pad_seq_tensor (labels , max_seqlen )
185- loss_mask = _pad_seq_tensor (loss_mask , max_seqlen )
186- position_ids = _pad_seq_tensor (position_ids , max_seqlen )
187156
188- target_cu_entries = max_num_seqs + 1
189- padded_params = PackedSeqParams (
190- qkv_format = packed_seq_params .qkv_format ,
191- cu_seqlens_q = _pad_cu_seqlens (packed_seq_params .cu_seqlens_q , target_cu_entries ),
192- cu_seqlens_kv = _pad_cu_seqlens (packed_seq_params .cu_seqlens_kv , target_cu_entries ),
193- cu_seqlens_q_padded = _pad_cu_seqlens (
194- packed_seq_params .cu_seqlens_q_padded , target_cu_entries
195- ),
196- cu_seqlens_kv_padded = _pad_cu_seqlens (
197- packed_seq_params .cu_seqlens_kv_padded , target_cu_entries
198- ),
199- max_seqlen_q = max_seqlen_global ,
200- max_seqlen_kv = max_seqlen_global ,
201- local_cp_size = packed_seq_params .local_cp_size ,
202- cp_group = packed_seq_params .cp_group ,
203- )
157+ if target_len is None :
158+ assert alignment is not None , "Either target_len or alignment must be provided."
159+ global_target_len = _round_up_to_alignment (int (actual_T ), alignment )
160+ else :
161+ global_target_len = int (target_len ) * cp_size
204162
205163 if cp_size > 1 :
206164 from megatron .core .extensions .transformer_engine import get_thd_partitioned_indices
207165
208166 if actual_T_is_local :
209167 local_actual_T = int (actual_T )
210- local_max_seqlen = int (max_seqlen )
168+ local_target_len = (
169+ int (target_len )
170+ if target_len is not None
171+ else _round_up_to_alignment (local_actual_T , alignment )
172+ )
211173 else :
212174 local_actual_T = int (
213175 get_thd_partitioned_indices (
@@ -221,22 +183,125 @@ def pad_thd_for_cuda_graph(
221183 cp_rank ,
222184 ).numel ()
223185 )
224- local_max_seqlen = int (
186+ local_target_len = int (
225187 get_thd_partitioned_indices (
226188 (
227- padded_params .cu_seqlens_q_padded
228- if padded_params .cu_seqlens_q_padded is not None
229- else padded_params .cu_seqlens_q
189+ packed_seq_params .cu_seqlens_q_padded
190+ if packed_seq_params .cu_seqlens_q_padded is not None
191+ else packed_seq_params .cu_seqlens_q
230192 ),
231- max_seqlen_global ,
193+ global_target_len ,
232194 cp_size ,
233195 cp_rank ,
234196 ).numel ()
235197 )
236- padding_mask = (
237- torch .arange (local_max_seqlen , device = mask_device ).unsqueeze (0 ) >= local_actual_T
238- )
239198 else :
240- padding_mask = torch .arange (max_seqlen , device = mask_device ).unsqueeze (0 ) >= actual_T
199+ local_actual_T = int (actual_T )
200+ local_target_len = global_target_len
201+
202+ return (
203+ int (actual_T ),
204+ local_actual_T ,
205+ local_target_len ,
206+ global_target_len ,
207+ mask_device ,
208+ actual_T_is_local ,
209+ )
210+
211+
212+ def pad_sequence_for_thd (
213+ tokens : Optional [Tensor ],
214+ labels : Optional [Tensor ],
215+ loss_mask : Optional [Tensor ],
216+ position_ids : Optional [Tensor ],
217+ packed_seq_params : PackedSeqParams ,
218+ alignment : Optional [int ] = None ,
219+ target_len : Optional [int ] = None ,
220+ max_num_seqs : Optional [int ] = None ,
221+ ) -> Tuple [
222+ Optional [Tensor ],
223+ Optional [Tensor ],
224+ Optional [Tensor ],
225+ Optional [Tensor ],
226+ PackedSeqParams ,
227+ Optional [Tensor ],
228+ ]:
229+ """Pad packed THD tensors after packing.
230+
231+ This appends padding tokens to token-like tensors and returns a padding mask
232+ for MoE auxiliary-loss/routing paths. When ``max_num_seqs`` is provided, the
233+ four cu_seqlens tensors are also padded to ``max_num_seqs + 1`` entries;
234+ this is required by CUDA Graph replay because those tensors are graph inputs.
235+
236+ Returns:
237+ Padded (tokens, labels, loss_mask, position_ids, packed_seq_params, padding_mask)
238+ padding_mask: [1, target] bool tensor, True at padding positions.
239+ """
240+ assert (alignment is None ) != (target_len is None ), (
241+ "Exactly one of alignment or target_len must be provided for THD padding."
242+ )
243+
244+ actual_T , local_actual_T , local_target_len , global_target_len , mask_device , _ = (
245+ _resolve_thd_padding_lengths (
246+ tokens ,
247+ labels ,
248+ loss_mask ,
249+ position_ids ,
250+ packed_seq_params ,
251+ target_len = target_len ,
252+ alignment = alignment ,
253+ )
254+ )
255+
256+ if actual_T is not None and packed_seq_params .cu_seqlens_q is not None :
257+ _cu = packed_seq_params .cu_seqlens_q
258+ _individual_lens = _cu [1 :] - _cu [:- 1 ]
259+ _max_individual = int (_individual_lens .max ().item ()) if _individual_lens .numel () > 0 else 0
260+ assert _max_individual <= global_target_len , (
261+ f"Individual request length ({ _max_individual } ) exceeds the global max sequence length "
262+ f"({ global_target_len } ). Increase --max-seqlen-per-dp-cp-rank / alignment, "
263+ f"or filter out overlong requests."
264+ )
265+
266+ tokens = _pad_seq_tensor (tokens , local_target_len )
267+ labels = _pad_seq_tensor (labels , local_target_len )
268+ loss_mask = _pad_seq_tensor (loss_mask , local_target_len )
269+ position_ids = _pad_seq_tensor (position_ids , local_target_len )
270+
271+ target_cu_entries = None if max_num_seqs is None else max_num_seqs + 1
272+ padded_params = PackedSeqParams (
273+ qkv_format = packed_seq_params .qkv_format ,
274+ cu_seqlens_q = (
275+ packed_seq_params .cu_seqlens_q
276+ if target_cu_entries is None
277+ else _pad_cu_seqlens (packed_seq_params .cu_seqlens_q , target_cu_entries )
278+ ),
279+ cu_seqlens_kv = (
280+ packed_seq_params .cu_seqlens_kv
281+ if target_cu_entries is None
282+ else _pad_cu_seqlens (packed_seq_params .cu_seqlens_kv , target_cu_entries )
283+ ),
284+ cu_seqlens_q_padded = (
285+ packed_seq_params .cu_seqlens_q_padded
286+ if target_cu_entries is None
287+ else _pad_cu_seqlens (packed_seq_params .cu_seqlens_q_padded , target_cu_entries )
288+ ),
289+ cu_seqlens_kv_padded = (
290+ packed_seq_params .cu_seqlens_kv_padded
291+ if target_cu_entries is None
292+ else _pad_cu_seqlens (packed_seq_params .cu_seqlens_kv_padded , target_cu_entries )
293+ ),
294+ max_seqlen_q = (
295+ global_target_len if target_cu_entries is not None else packed_seq_params .max_seqlen_q
296+ ),
297+ max_seqlen_kv = (
298+ global_target_len if target_cu_entries is not None else packed_seq_params .max_seqlen_kv
299+ ),
300+ local_cp_size = packed_seq_params .local_cp_size ,
301+ cp_group = packed_seq_params .cp_group ,
302+ total_tokens = local_target_len if target_cu_entries is None else None ,
303+ )
304+
305+ padding_mask = torch .arange (local_target_len , device = mask_device ).unsqueeze (0 ) >= local_actual_T
241306
242307 return tokens , labels , loss_mask , position_ids , padded_params , padding_mask
0 commit comments