已合并
fboverlap supports swap unperm2 activation #2212
AtomGit-Bot创建于 2025年4月18日
fboverlap supports swap unperm2 activation #2212
已合并
AtomGit-Bot创建于 2025年4月18日
refs/pull/2212/head合入到core_r0.8.0
8 个文件变更+585-21
@@ -8,7 +8,7 @@ from megatron.core.transformer.moe.moe_utils import permute, unpermute
8from megatron.core.tensor_parallel.mappings import _gather_along_first_dim_expert_parallel8from megatron.core.tensor_parallel.mappings import _gather_along_first_dim_expert_parallel
9from megatron.core.utils import make_viewless_tensor9from megatron.core.utils import make_viewless_tensor
10from megatron.training import get_args10from megatron.training import get_args
11-from mindspeed.core.transformer.moe.unpermute_without_activation import UnpermuteWithoutActivation11+from mindspeed.core.pipeline_parallel.fb_overlap.modules.utils import TensorSwapManager
12 12 
13 13 
14def preprocess(self, indices: torch.Tensor) -> torch.Tensor:14def preprocess(self, indices: torch.Tensor) -> torch.Tensor:
@@ -269,19 +269,38 @@ def alltoall_token_unperm1(
269def alltoall_token_unperm2(self, permutated_local_input_tokens, probs=None):269def alltoall_token_unperm2(self, permutated_local_input_tokens, probs=None):
270 # Unpermutation 1: AlltoAll output to output270 # Unpermutation 1: AlltoAll output to output
271 271 
272+ args = get_args()
272 probs = probs if probs is not None else self.probs273 probs = probs if probs is not None else self.probs
273- output = unpermute(274+ if args.moe_unperm2_mem_optim_swap:
275+ probs = None
276+ output_unperm = unpermute(
274 permutated_local_input_tokens,277 permutated_local_input_tokens,
275 self.reversed_local_input_permutation_mapping,278 self.reversed_local_input_permutation_mapping,
276 probs=probs,279 probs=probs,
277 padded_mode=self.drop_and_pad,280 padded_mode=self.drop_and_pad,
278 restore_shape=self.hiddden_shape_before_permute,281 restore_shape=self.hiddden_shape_before_permute,
279 )282 )
280- if get_args().moe_unperm2_mem_optim:283+ output_swap_manager = None
281- output = output.view(-1, self.router_topk, output.shape[-1])284+ if args.moe_unperm2_mem_optim:
282- output = output.sum(dim=1)285+ output_unperm = output_unperm.view(-1, self.router_topk, output_unperm.shape[-1])
283- 286+ output = output_unperm.sum(dim=1)
287+ elif args.moe_unperm2_mem_optim_swap:
288+ permutated_local_input_tokens.untyped_storage().resize_(0)
289+ self.probs_detached = self.probs.detach()
290+ assert not self.probs_detached.requires_grad
284 291 
292+ # [s, k, h] * [s, k, 1] -> [s, k, h]
293+ # forward pass here is broadcast and mul
294+ # backward pass is mul and reducesum
295+ weighted_output = output_unperm.view(-1, self.router_topk, output_unperm.shape[-1]) * self.probs_detached.unsqueeze(-1)
296+ # swap output
297+ TensorSwapManager.wait_all_swap_out('unperm2')
298+ output_swap_manager = TensorSwapManager(output_unperm, 'unperm2')
299+ output_swap_manager.async_swap_out(wait_stream=torch.npu.current_stream())
300+ output = weighted_output.sum(dim=1).to(permutated_local_input_tokens.dtype) # [s, h]
301+ else:
302+ # cast for unfused unpermute
303+ output = output_unperm.to(permutated_local_input_tokens.dtype)
285 # Reshape the output tensor304 # Reshape the output tensor
286 output = output.view(self.hidden_shape)305 output = output.view(self.hidden_shape)
287 306 
@@ -289,5 +308,4 @@ def alltoall_token_unperm2(self, permutated_local_input_tokens, probs=None):
289 inp=output, requires_grad=output.requires_grad, keep_graph=True308 inp=output, requires_grad=output.requires_grad, keep_graph=True
290 )309 )
291 310 
292- 311+ return output, output_swap_manager
293- return output, None
@@ -1,3 +1,7 @@
1+# Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved.
2+ 
3+from typing import Optional, Dict, List
A
Aaijgnem12025年4月18日

copyright

likedislike
4+ 
1import torch5import torch
2from torch.autograd.variable import Variable6from torch.autograd.variable import Variable
3from megatron.core.pipeline_parallel import p2p_communication7from megatron.core.pipeline_parallel import p2p_communication
@@ -141,3 +145,164 @@ def p2p_comm_helper(comm_params: P2PCommParams, tensor_tosend):
141 else:145 else:
142 return None, p2p_handles146 return None, p2p_handles
143 147 
148+ 
149+class TensorSwapManager:
150+ """Manager for asynchronous tensor swapping between NPU and CPU memory."""
151+
152+ _SWAP_OUT_STREAM = None
153+ _SWAP_IN_STREAM = None
154+ _ALL_SWAP_OUT_QUEUES = {} # Dictionary of swap groups for coordinated operations
155+ 
156+ @classmethod
157+ def _get_swap_out_stream(cls):
158+ """Get or create the swap-out stream."""
159+ if cls._SWAP_OUT_STREAM is None:
160+ cls._SWAP_OUT_STREAM = torch.npu.Stream(device=torch.npu.current_device())
161+ return cls._SWAP_OUT_STREAM
162+ 
163+ @classmethod
164+ def _get_swap_in_stream(cls):
165+ """Get or create the swap-in stream."""
166+ if cls._SWAP_IN_STREAM is None:
167+ cls._SWAP_IN_STREAM = torch.npu.Stream(device=torch.npu.current_device())
168+ return cls._SWAP_IN_STREAM
169+ 
170+ def __init__(self, tensor, swap_group_name=None):
171+ """
172+ Initialize a tensor swap manager.
173+
174+ Args:
175+ tensor: The NPU tensor to swap
176+ swap_group_name: Optional group name for coordinated swap operations
177+ """
178+ # Check if tensor is a slice (requires special handling, not supported yet)
179+ if tensor.storage().size() != tensor.numel():
180+ raise AssertionError('TensorSwapManager cannot handle sliced tensors')
181+ 
182+ self.npu_tensor = tensor
183+ self.cpu_tensor = None
184+ self.swap_out_event = None
H
Hharygo22025年4月18日

注释可删除

likedislike
185+ self.swap_in_event = None
186+ 
187+ self.swap_group_name = swap_group_name # Group for coordinated operations
188+ self.under_swap_in = False
189+ 
190+ # Initialize queue for this swap group if it doesn't exist
191+ if swap_group_name and swap_group_name not in self._ALL_SWAP_OUT_QUEUES:
192+ self._ALL_SWAP_OUT_QUEUES[swap_group_name] = []
193+ 
194+ def async_swap_out(self, wait_event=None, wait_stream=None):
195+ """
196+ Asynchronously copy tensor from NPU to CPU memory.
197+
198+ Args:
199+ event: Optional event to wait for before swapping
200+ stream: Optional stream to synchronize with before swapping
201+ """
202+ swap_stream = self._get_swap_out_stream()
203+ # Allocate pinned CPU memory (enables faster async transfers)
204+ self.cpu_tensor = torch.empty_like(self.npu_tensor,
205+ pin_memory=True,
206+ device='cpu')
207+
208+ with torch.npu.stream(swap_stream):
209+ if wait_event:
210+ swap_stream.wait_event(wait_event)
211+ if wait_stream:
212+ swap_stream.wait_stream(wait_stream)
213+ 
214+ self.cpu_tensor.untyped_storage().copy_(
215+ self.npu_tensor.untyped_storage(),
216+ non_blocking=True)
217+
218+ self.swap_out_event = torch.npu.Event()
219+ self.swap_out_event.record()
220+
221+ # Add to swap group if specified
222+ if self.swap_group_name:
223+ self._ALL_SWAP_OUT_QUEUES[self.swap_group_name].append(self)
224+ 
225+ def wait_swap_out(self):
226+ """Wait for swap-out to complete and release NPU memory."""
227+ if self.swap_out_event:
228+ torch.npu.current_stream().wait_event(self.swap_out_event)
229+ # Release NPU memory (but keep storage object alive)
230+ if not self.under_swap_in:
231+ self.npu_tensor.untyped_storage().resize_(0)
232+ 
233+ def async_swap_in(self, wait_event=None, wait_stream=None):
234+ """
235+ Asynchronously copy tensor from CPU back to NPU memory.
236+
237+ Args:
238+ event: Optional event to wait for before swapping
239+ stream: Optional stream to synchronize with before swapping
240+ """
241+ self.under_swap_in = True
242+ if self.npu_tensor.untyped_storage().size() != 0:
243+ return
244+ swap_stream = self._get_swap_in_stream()
245+ # Ensure NPU storage is properly sized
246+ self.npu_tensor.untyped_storage().resize_(
247+ self.cpu_tensor.untyped_storage().size())
248+ # Wait for previous swap-out to complete
249+ torch.npu.current_stream().wait_event(self.swap_out_event)
250+
251+ with torch.npu.stream(swap_stream):
252+ if wait_event:
253+ swap_stream.wait_event(wait_event)
254+ if wait_stream:
255+ swap_stream.wait_stream(wait_stream)
256+ 
257+ self.npu_tensor.untyped_storage().copy_(
258+ self.cpu_tensor.untyped_storage(),
259+ non_blocking=True)
260+
261+ self.swap_in_event = torch.npu.Event()
262+ self.swap_in_event.record()
263+ 
264+ def wait_swap_in(self):
265+ """Wait for swap-in to complete and release CPU memory."""
266+ if self.swap_in_event:
267+ torch.npu.current_stream().wait_event(self.swap_in_event)
268+ # Release CPU memory
269+ self.cpu_tensor = None
270+ self.under_swap_in = False
271+ 
272+ @classmethod
273+ def wait_all_swap_out(cls, group_name):
274+ """
275+ Wait for all swap-out operations in a group to complete.
276+
277+ Args:
278+ group_name: Name of the swap group to synchronize
279+ """
280+ if group_name in cls._ALL_SWAP_OUT_QUEUES:
281+ for manager in cls._ALL_SWAP_OUT_QUEUES[group_name]:
282+ manager.wait_swap_out()
283+ # Clear the group while maintaining the list object
284+ cls._ALL_SWAP_OUT_QUEUES[group_name].clear()
285+ 
286+ 
287+def make_wait_swap_in_hook(swap_manager):
288+ """
289+ Create a hook that waits for a swap-in operation to complete.
290+
291+ Returns:
292+ A callable hook function that waits for swap-in completion
293+ """
294+ return lambda *_: swap_manager.wait_swap_in()
295+ 
296+ 
297+def make_async_swap_in_hook(swap_managers):
298+ """
299+ Create a hook that initiates async swap-in for multiple tensors.
300+
301+ Args:
302+ swap_managers: List of TensorSwapManager instances
303+
304+ Returns:
305+ A callable hook function that triggers swap-in for all managers
306+ """
307+ return lambda *_: [m.async_swap_in(wait_stream=torch.npu.current_stream())
308+ for m in swap_managers]
@@ -22,6 +22,11 @@ def transformer_layer_backward_moe(
22 dispached_input, probs, indices, global_input_tokens_local_experts_indices = self.recompute_needed_tensors22 dispached_input, probs, indices, global_input_tokens_local_experts_indices = self.recompute_needed_tensors
23 ep_group = parallel_state.get_expert_model_parallel_group()23 ep_group = parallel_state.get_expert_model_parallel_group()
24 tp_size = parallel_state.get_tensor_model_parallel_world_size()24 tp_size = parallel_state.get_tensor_model_parallel_world_size()
25+ 
26+ # Launch swap-in at the beginning of the backward pass.
27+ if self.unperm2_swap_manager:
28+ self.unperm2_swap_manager.async_swap_in(wait_stream=torch.npu.current_stream())
29+ 
25 if args.moe_tp_extend_ep:30 if args.moe_tp_extend_ep:
26 ep_group = parallel_state.get_tensor_and_expert_parallel_group()31 ep_group = parallel_state.get_tensor_and_expert_parallel_group()
27 if tp_size > 1:32 if tp_size > 1:
@@ -37,7 +42,8 @@ def transformer_layer_backward_moe(
37 if backward_ag_shared_handle is not None:42 if backward_ag_shared_handle is not None:
38 backward_ag_shared_handle.wait()43 backward_ag_shared_handle.wait()
39 backward_ag_shared_handle = None44 backward_ag_shared_handle = None
40- if layer_output_grad is not None:45+ # In case of unperm2 swap, layer_output_grad is required for probs_grad before router-backward
46+ if layer_output_grad is not None and not args.moe_unperm2_mem_optim_swap:
41 layer_output_grad.untyped_storage().resize_(0)47 layer_output_grad.untyped_storage().resize_(0)
42 _, unperm1_out_grad, handle = async_all_to_all(48 _, unperm1_out_grad, handle = async_all_to_all(
43 self.unperm_a2a_graph[1].grad,49 self.unperm_a2a_graph[1].grad,
@@ -117,7 +123,22 @@ def transformer_layer_backward_moe(
117 if prob_handle:123 if prob_handle:
118 prob_handle.wait()124 prob_handle.wait()
119 run_graph_backward(self.perm1_graph, [perm1_out_grad, perm1_prob_out_grad])125 run_graph_backward(self.perm1_graph, [perm1_out_grad, perm1_prob_out_grad])
120- run_graph_backward(self.router_graph)126+ 
127+ # Swap-in unperm2 input for probs_grad computation in backward pass of router.
128+ if self.unperm2_swap_manager:
129+ self.unperm2_swap_manager.wait_swap_in()
130+ probs_grad = None
131+ if args.moe_unperm2_mem_optim_swap:
132+ # dprobs computation
133+ H = self.unperm2_swap_manager.npu_tensor.shape[-1]
134+ K = args.moe_router_topk
135+ probs_dtype = probs.dtype
136+ probs_grad = layer_output_grad.to(probs_dtype) * self.unperm2_swap_manager.npu_tensor.reshape(-1, K, H).to(probs_dtype)
137+ probs_grad = probs_grad.sum(dim=-1)
138+ layer_output_grad.untyped_storage().resize_(0)
139+ self.unperm2_swap_manager.npu_tensor.untyped_storage().resize_(0)
140+ run_graph_backward(self.router_graph, probs_grad)
141+ 
121 run_graph_backward(self.pre_mlp_layernorm_graph)142 run_graph_backward(self.pre_mlp_layernorm_graph)
122 run_graph_backward(self.attn_graph)143 run_graph_backward(self.attn_graph)
123 144 
@@ -178,7 +178,7 @@ def transformer_layer_forward_moe(
178 # but backward func of unperm1_out is needed, so resize the storage but keep tensor.178 # but backward func of unperm1_out is needed, so resize the storage but keep tensor.
179 unperm1_out.untyped_storage().resize_(0)179 unperm1_out.untyped_storage().resize_(0)
180 detached_unperm_a2a_out = detach_tensor(unperm_a2a_out, checkpoint_forward=checkpoint)180 detached_unperm_a2a_out = detach_tensor(unperm_a2a_out, checkpoint_forward=checkpoint)
181- route_expert_output, _ = alltoall_token_unperm2(self.mlp.token_dispatcher, detached_unperm_a2a_out)181+ route_expert_output, unperm2_swap_manager = alltoall_token_unperm2(self.mlp.token_dispatcher, detached_unperm_a2a_out)
182 if get_args().moe_unperm2_mem_optim:182 if get_args().moe_unperm2_mem_optim:
183 unperm_a2a_out.untyped_storage().resize_(0)183 unperm_a2a_out.untyped_storage().resize_(0)
184 184 
@@ -191,7 +191,7 @@ def transformer_layer_forward_moe(
191 share_experts_graph = None191 share_experts_graph = None
192 mlp_output = route_expert_output192 mlp_output = route_expert_output
193 193 
194- if recomp_norm:194+ if recomp_norm and mlp_output.requires_grad:
195 mlp_output.register_hook(self.norm_ckpt2.recompute)195 mlp_output.register_hook(self.norm_ckpt2.recompute)
196 196 
197 197 
@@ -232,6 +232,7 @@ def transformer_layer_forward_moe(
232 checkpointed=checkpoint232 checkpointed=checkpoint
233 )233 )
234 graph.act_ckpt_manager = act_ckpt_manager234 graph.act_ckpt_manager = act_ckpt_manager
235+ graph.unperm2_swap_manager = unperm2_swap_manager
235 236 
236 return output, context, graph237 return output, context, graph
237 238 
@@ -283,7 +284,8 @@ def transformer_layer_forward_dense(
283 284 
284 if recomp_norm:285 if recomp_norm:
285 self.norm_ckpt2.discard_output()286 self.norm_ckpt2.discard_output()
286- mlp_output_with_bias[0].register_hook(self.norm_ckpt2.recompute)287+ if mlp_output_with_bias[0].requires_grad:
288+ mlp_output_with_bias[0].register_hook(self.norm_ckpt2.recompute)
287 289 
288 290 
289 with self.bias_dropout_add_exec_handler():291 with self.bias_dropout_add_exec_handler():
@@ -63,6 +63,10 @@ def transformer_layer_forward_dense_backward_moe_overlaping(
63 recomp_norm = getattr(args, 'recompute_norm', False)63 recomp_norm = getattr(args, 'recompute_norm', False)
64 bwd_dispached_input, bwd_probs, bwd_indices, global_input_tokens_local_experts_indices = bwd_layer_graph.recompute_needed_tensors64 bwd_dispached_input, bwd_probs, bwd_indices, global_input_tokens_local_experts_indices = bwd_layer_graph.recompute_needed_tensors
65 65 
66+ # Launch swap-in at the beginning of the backward pass.
67+ if bwd_layer_graph.unperm2_swap_manager:
68+ bwd_layer_graph.unperm2_swap_manager.async_swap_in(wait_stream=torch.npu.current_stream())
69+ 
66 # Unperm2 Bwd70 # Unperm2 Bwd
67 # check if backward unpermutation alltoall is launched at bwd layer before71 # check if backward unpermutation alltoall is launched at bwd layer before
68 if bwd_unperm_a2a_handle is None:72 if bwd_unperm_a2a_handle is None:
@@ -200,7 +204,25 @@ def transformer_layer_forward_dense_backward_moe_overlaping(
200 shared_expert_grad.untyped_storage().resize_(0)204 shared_expert_grad.untyped_storage().resize_(0)
201 run_graph_backward(bwd_layer_graph.shared_experts_graph, backward_ag_shared, keep_grad=True) # dw computation205 run_graph_backward(bwd_layer_graph.shared_experts_graph, backward_ag_shared, keep_grad=True) # dw computation
202 WeightGradStore.end_decouple()206 WeightGradStore.end_decouple()
203- run_graph_backward(bwd_layer_graph.router_graph)207+ 
208+ # swap-in unperm2 input for probs_grad computation
209+ if bwd_layer_graph.unperm2_swap_manager:
210+ bwd_layer_graph.unperm2_swap_manager.wait_swap_in()
211+ probs_grad = None
212+ if args.moe_unperm2_mem_optim_swap:
213+ # dprobs computation
214+ output_grad = bwd_layer_output_grad
215+ if hasattr(bwd_layer_graph, 'last_layer_input_grad'):
216+ output_grad = bwd_layer_graph.last_layer_input_grad
217+ H = bwd_layer_graph.unperm2_swap_manager.npu_tensor.shape[-1]
218+ K = args.moe_router_topk
219+ probs_dtype = bwd_probs.dtype
220+ probs_grad = bwd_layer_graph.unperm2_swap_manager.npu_tensor.reshape(-1, K, H).to(probs_dtype) * output_grad.to(probs_dtype)
221+ output_grad.untyped_storage().resize_(0)
222+ bwd_layer_graph.unperm2_swap_manager.npu_tensor.untyped_storage().resize_(0)
223+ probs_grad = probs_grad.sum(dim=-1)
224+ run_graph_backward(bwd_layer_graph.router_graph, probs_grad)
225+ 
204 run_graph_backward(bwd_layer_graph.pre_mlp_layernorm_graph, keep_graph=True)226 run_graph_backward(bwd_layer_graph.pre_mlp_layernorm_graph, keep_graph=True)
205 WeightGradStore.start_decouple()227 WeightGradStore.start_decouple()
206 run_graph_backward(bwd_layer_graph.attn_graph, keep_grad=True)228 run_graph_backward(bwd_layer_graph.attn_graph, keep_grad=True)
@@ -272,6 +294,13 @@ def transformer_layer_forward_dense_backward_moe_overlaping(
272 checkpointed=checkpoint294 checkpointed=checkpoint
273 )295 )
274 296 
297+ # save original layer output for probs_grad computation
298+ if args.moe_unperm2_mem_optim_swap \
299+ and next_bwd_layer_graph is not None \
300+ and getattr(next_bwd_layer_graph, 'is_moe_layer', False):
301+
302+ next_bwd_layer_graph.last_layer_input_grad = bwd_layer_graph.layer_input.grad
303+ 
275 for tensor in bwd_layer_graph.recompute_needed_tensors:304 for tensor in bwd_layer_graph.recompute_needed_tensors:
276 if tensor is not None:305 if tensor is not None:
277 tensor.untyped_storage().resize_(0)306 tensor.untyped_storage().resize_(0)
@@ -457,7 +486,7 @@ def transformer_layer_forward_moe_backward_dense_overlaping(
457 486 
458 with checkpoint_context:487 with checkpoint_context:
459 detached_unperm_a2a_out = detach_tensor(unperm_a2a_out, checkpoint_forward=checkpoint)488 detached_unperm_a2a_out = detach_tensor(unperm_a2a_out, checkpoint_forward=checkpoint)
460- route_expert_output, _ = alltoall_token_unperm2(fwd_layer.mlp.token_dispatcher, detached_unperm_a2a_out)489+ route_expert_output, unperm2_swap_manager = alltoall_token_unperm2(fwd_layer.mlp.token_dispatcher, detached_unperm_a2a_out)
461 if args.moe_unperm2_mem_optim:490 if args.moe_unperm2_mem_optim:
462 unperm_a2a_out.untyped_storage().resize_(0)491 unperm_a2a_out.untyped_storage().resize_(0)
463 492 
@@ -522,6 +551,7 @@ def transformer_layer_forward_moe_backward_dense_overlaping(
522 checkpointed=checkpoint551 checkpointed=checkpoint
523 )552 )
524 graph.act_ckpt_manager = act_ckpt_manager553 graph.act_ckpt_manager = act_ckpt_manager
554+ graph.unperm2_swap_manager = unperm2_swap_manager
525 555 
526 for tensor in bwd_layer_graph.recompute_needed_tensors:556 for tensor in bwd_layer_graph.recompute_needed_tensors:
527 if tensor is not None:557 if tensor is not None:
@@ -702,13 +732,18 @@ def transformer_layer_forward_moe_backward_moe_overlaping(
702 tp_group = parallel_state.get_tensor_model_parallel_group()732 tp_group = parallel_state.get_tensor_model_parallel_group()
703 use_shared_experts = hasattr(fwd_layer.mlp, 'shared_experts') and fwd_layer.mlp.shared_experts is not None733 use_shared_experts = hasattr(fwd_layer.mlp, 'shared_experts') and fwd_layer.mlp.shared_experts is not None
704 recomp_norm = getattr(args, 'recompute_norm', False)734 recomp_norm = getattr(args, 'recompute_norm', False)
735+ swap_unperm2 = getattr(args, 'moe_unperm2_mem_optim_swap', False)
705 bwd_dispached_input, bwd_probs, bwd_indices, global_input_tokens_local_experts_indices = bwd_layer_graph.recompute_needed_tensors736 bwd_dispached_input, bwd_probs, bwd_indices, global_input_tokens_local_experts_indices = bwd_layer_graph.recompute_needed_tensors
706 a2a_hooked_on_attention = getattr(fwd_layer.self_attention, 'a2a_hooked_on_attention', False)737 a2a_hooked_on_attention = getattr(fwd_layer.self_attention, 'a2a_hooked_on_attention', False)
707 738 
739+ # Launch swap-in
740+ if bwd_layer_graph.unperm2_swap_manager:
741+ bwd_layer_graph.unperm2_swap_manager.async_swap_in(wait_stream=torch.npu.current_stream())
742+ 
708 # Unperm2 Bwd743 # Unperm2 Bwd
709 # check if backward unpermutation alltoall is launched at bwd layer before744 # check if backward unpermutation alltoall is launched at bwd layer before
710 if bwd_unperm_a2a_handle is None:745 if bwd_unperm_a2a_handle is None:
711- run_graph_backward(bwd_layer_graph.unperm2_graph, bwd_layer_output_grad)746+ run_graph_backward(bwd_layer_graph.unperm2_graph, bwd_layer_output_grad, keep_grad=swap_unperm2)
712 # Async Unperm A2A747 # Async Unperm A2A
713 if tp_size > 1 and a2a_hooked_on_attention:748 if tp_size > 1 and a2a_hooked_on_attention:
714 set_async_alltoall_inputs(749 set_async_alltoall_inputs(
@@ -959,7 +994,25 @@ def transformer_layer_forward_moe_backward_moe_overlaping(
959 bwd_prob_handle.wait()994 bwd_prob_handle.wait()
960 run_graph_backward(bwd_layer_graph.perm1_graph, [perm1_out_grad, perm1_prob_out_grad])995 run_graph_backward(bwd_layer_graph.perm1_graph, [perm1_out_grad, perm1_prob_out_grad])
961 perm1_out_grad.untyped_storage().resize_(0)996 perm1_out_grad.untyped_storage().resize_(0)
962- run_graph_backward(bwd_layer_graph.router_graph)997+ 
998+ # router backward
999+ if bwd_layer_graph.unperm2_swap_manager:
1000+ bwd_layer_graph.unperm2_swap_manager.wait_swap_in()
1001+ probs_grad = None
1002+ if swap_unperm2:
1003+ # dprobs computation
1004+ output_grad = bwd_layer_output_grad
1005+ if hasattr(bwd_layer_graph, 'last_layer_input_grad'):
1006+ output_grad = bwd_layer_graph.last_layer_input_grad
1007+ H = bwd_layer_graph.unperm2_swap_manager.npu_tensor.shape[-1]
1008+ K = args.moe_router_topk
1009+ probs_dtype = bwd_probs.dtype
1010+ probs_grad = bwd_layer_graph.unperm2_swap_manager.npu_tensor.reshape(-1, K, H).to(probs_dtype) * output_grad.to(probs_dtype)
1011+ output_grad.untyped_storage().resize_(0)
1012+ bwd_layer_graph.unperm2_swap_manager.npu_tensor.untyped_storage().resize_(0)
1013+ probs_grad = probs_grad.sum(dim=-1)
1014+ run_graph_backward(bwd_layer_graph.router_graph, probs_grad)
1015+ 
963 run_graph_backward(bwd_layer_graph.pre_mlp_layernorm_graph, keep_graph=True)1016 run_graph_backward(bwd_layer_graph.pre_mlp_layernorm_graph, keep_graph=True)
964 WeightGradStore.start_decouple()1017 WeightGradStore.start_decouple()
965 run_graph_backward(bwd_layer_graph.attn_graph, keep_grad=True)1018 run_graph_backward(bwd_layer_graph.attn_graph, keep_grad=True)
@@ -968,7 +1021,7 @@ def transformer_layer_forward_moe_backward_moe_overlaping(
968 unperm_a2a_out, unperm_a2a_handle = get_async_alltoall_outputs()1021 unperm_a2a_out, unperm_a2a_handle = get_async_alltoall_outputs()
969 1022 
970 if next_bwd_layer_graph is not None and getattr(next_bwd_layer_graph, 'is_moe_layer', False):1023 if next_bwd_layer_graph is not None and getattr(next_bwd_layer_graph, 'is_moe_layer', False):
971- run_graph_backward(next_bwd_layer_graph.unperm2_graph, bwd_layer_graph.layer_input.grad, keep_graph=True)1024+ run_graph_backward(next_bwd_layer_graph.unperm2_graph, bwd_layer_graph.layer_input.grad, keep_graph=True, keep_grad=swap_unperm2)
972 1025 
973 unperm_a2a_handle.wait()1026 unperm_a2a_handle.wait()
974 unperm_a2a_handle = None1027 unperm_a2a_handle = None
@@ -984,7 +1037,7 @@ def transformer_layer_forward_moe_backward_moe_overlaping(
984 )1037 )
985 with checkpoint_context:1038 with checkpoint_context:
986 detached_unperm_a2a_out = detach_tensor(unperm_a2a_out)1039 detached_unperm_a2a_out = detach_tensor(unperm_a2a_out)
987- route_expert_output, _ = alltoall_token_unperm2(fwd_layer.mlp.token_dispatcher, detached_unperm_a2a_out)1040+ route_expert_output, unperm2_swap_manager = alltoall_token_unperm2(fwd_layer.mlp.token_dispatcher, detached_unperm_a2a_out)
988 if args.moe_unperm2_mem_optim:1041 if args.moe_unperm2_mem_optim:
989 unperm_a2a_out.untyped_storage().resize_(0)1042 unperm_a2a_out.untyped_storage().resize_(0)
990 1043 
@@ -1050,6 +1103,14 @@ def transformer_layer_forward_moe_backward_moe_overlaping(
1050 checkpointed=checkpoint1103 checkpointed=checkpoint
1051 )1104 )
1052 graph.act_ckpt_manager = act_ckpt_manager1105 graph.act_ckpt_manager = act_ckpt_manager
1106+ graph.unperm2_swap_manager = unperm2_swap_manager
1107+ 
1108+ # save original layer output for probs_grad computation
1109+ if swap_unperm2 \
1110+ and next_bwd_layer_graph is not None \
1111+ and getattr(next_bwd_layer_graph, 'is_moe_layer', False):
1112+
1113+ next_bwd_layer_graph.last_layer_input_grad = bwd_layer_graph.layer_input.grad
1053 1114 
1054 for tensor in bwd_layer_graph.recompute_needed_tensors:1115 for tensor in bwd_layer_graph.recompute_needed_tensors:
1055 if tensor is not None:1116 if tensor is not None:
@@ -13,6 +13,7 @@ class FwdBwdOverlapFeature(MindSpeedFeature):
13 group = parser.add_argument_group(title=self.feature_name)13 group = parser.add_argument_group(title=self.feature_name)
14 group.add_argument('--moe-fb-overlap', action='store_true')14 group.add_argument('--moe-fb-overlap', action='store_true')
15 group.add_argument('--moe-unperm2-mem-optim', action='store_true')15 group.add_argument('--moe-unperm2-mem-optim', action='store_true')
16+ group.add_argument('--moe-unperm2-mem-optim-swap', action='store_true')
16 17 
17 18 
18 19 
@@ -28,9 +29,9 @@ class FwdBwdOverlapFeature(MindSpeedFeature):
28 self.incompatible_check(args, 'recompute_in_advance')29 self.incompatible_check(args, 'recompute_in_advance')
29 self.incompatible_check(args, 'use_legacy_models')30 self.incompatible_check(args, 'use_legacy_models')
30 if args.moe_fb_overlap and args.moe_token_dispatcher_type == 'allgather':31 if args.moe_fb_overlap and args.moe_token_dispatcher_type == 'allgather':
31- raise AssertionError('The fb overlap feature do not support allgather dispatcher')32+ raise AssertionError('The fb overlap feature do not support allgather dispatcher.')
32 if args.moe_fb_overlap and args.moe_zero_memory == 'level1':33 if args.moe_fb_overlap and args.moe_zero_memory == 'level1':
33- raise AssertionError('fb overlap only support moe zero memory level 0')34+ raise AssertionError('fb overlap only support moe zero memory level 0.')
34 35 
35 self.dependency_check(args, 'n_shared_experts')36 self.dependency_check(args, 'n_shared_experts')
36 self.dependency_check(args, 'moe_permutation_async_comm')37 self.dependency_check(args, 'moe_permutation_async_comm')
@@ -38,6 +39,10 @@ class FwdBwdOverlapFeature(MindSpeedFeature):
38 self.dependency_check(args, 'moe_tp_extend_ep')39 self.dependency_check(args, 'moe_tp_extend_ep')
39 if args.moe_unperm2_mem_optim and not args.moe_fb_overlap:40 if args.moe_unperm2_mem_optim and not args.moe_fb_overlap:
Z
Zzhao-yifan272025年4月18日

这句没意义,上面三行应该回退掉?

likedislike
40 raise AssertionError('--moe-unperm2-mem-optim currently only can be used with --moe-fb-overlap')41 raise AssertionError('--moe-unperm2-mem-optim currently only can be used with --moe-fb-overlap')
42+ if args.moe_unperm2_mem_optim_swap and not args.moe_fb_overlap:
43+ raise AssertionError('--moe-unperm2-mem-optim-swap currently only can be used with --moe-fb-overlap')
44+ if args.moe_unperm2_mem_optim and args.moe_unperm2_mem_optim_swap:
45+ raise AssertionError('--moe-unperm2-mem-optim and --moe-unperm2-mem-optim-swap are incompatible.')
41 46 
42 47 
43 def register_patches(self, patch_manager, args):48 def register_patches(self, patch_manager, args):
@@ -0,0 +1,126 @@
1+ 
2+#!/bin/bash
3+ 
4+export CUDA_DEVICE_MAX_CONNECTIONS=1
5+source "tests_extend/system_tests/env_npu.sh"
6+ 
7+NPUS_PER_NODE=16
8+MASTER_ADDR=localhost
9+MASTER_PORT=6001
10+NNODES=1
11+NODE_RANK=0
12+WORLD_SIZE=$(($NPUS_PER_NODE*$NNODES))
13+ 
14+CKPT_DIR=./ckpt_llama
15+DATA_PATH="/home/dataset/llama2/alpaca_text_document"
16+TOKENIZER_MODEL="/home/dataset/model/llama-2-7b-hf/tokenizer.model"
17+ 
18+TP=2
19+PP=4
20+EP=2
21+ 
22+DISTRIBUTED_ARGS="
23+ --nproc_per_node $NPUS_PER_NODE \
24+ --nnodes $NNODES \
25+ --node_rank $NODE_RANK \
26+ --master_addr $MASTER_ADDR \
27+ --master_port $MASTER_PORT
28+"
29+ 
30+DUALPIPE_ARGS="
31+ --moe-unperm2-mem-optim-swap \
32+ --moe-fb-overlap \
33+ --schedules-method dualpipev \
34+ --moe-zero-memory level0 \
35+ --moe-tp-extend-ep \
36+"
37+ 
38+MOE_ARGS="
39+ --expert-model-parallel-size ${EP} \
40+ --moe-token-dispatcher-type alltoall \
41+ --moe-permutation-async-comm \
42+ -use-fused-moe-token-permute-and-unpermute \
43+ --moe-grouped-gemm \
44+ --n-shared-experts 1 \
45+ --num-experts 32 \
46+ --moe-router-topk 8 \
47+ --moe-aux-loss-coeff 0.02 \
48+ --moe-tp-extend-ep \
49+ --recompute-norm \
50+ --recompute-activation-function \
51+"
52+ 
53+GPT_ARGS="
54+ --tensor-model-parallel-size ${TP} \
55+ --pipeline-model-parallel-size ${PP} \
56+ --use-flash-attn \
57+ --use-fused-rotary-pos-emb \
58+ --use-fused-swiglu \
59+ --use-fused-rmsnorm \
60+ --sequence-parallel \
61+ --use-distributed-optimizer \
62+ --num-layers 8 \
63+ --noop-layers 7 \
64+ --manual-gc \
65+ --manual-gc-interval 50 \
66+ --seq-length 4096 \
67+ --max-position-embeddings 4096 \
68+ --train-iters 50 \
69+ --hidden-size 5120 \
70+ --num-attention-heads 128 \
71+ --ffn-hidden-size 1536 \
72+ --make-vocab-size-divisible-by 128 \
73+ --vocab-size 126464 \
74+ --micro-batch-size 1 \
75+ --global-batch-size 32 \
76+ --tokenizer-type Llama2Tokenizer \
77+ --tokenizer-model ${TOKENIZER_MODEL} \
78+ --disable-bias-linear \
79+ --lr-decay-style linear \
80+ --lr-warmup-iters 0 \
81+ --short-seq-prob 0.0 \
82+ --attention-dropout 0.0 \
83+ --hidden-dropout 0.0 \
84+ --untie-embeddings-and-output-weights \
85+ --init-method-std 0.006 \
86+ --position-embedding-type rope \
87+ --normalization RMSNorm \
88+ --swiglu \
89+ --no-masked-softmax-fusion \
90+ --attention-softmax-in-fp32 \
91+ --no-gradient-accumulation-fusion \
92+ --bf16 \
93+ --group-query-attention \
94+ --num-query-groups 8 \
95+ --lr 2.0e-4 \
96+ --min-lr 2.0e-4 \
97+ --weight-decay 0.1 \
98+ --clip-grad 1.0 \
99+ --adam-beta1 0.9 \
100+ --adam-beta2 0.95 \
101+ --rotary-base 100000 \
102+ --norm-epsilon 1.0e-5 \
103+"
104+ 
105+DATA_ARGS="
106+ --data-path $DATA_PATH \
107+ --split 995,5,0
108+"
109+ 
110+OUTPUT_ARGS="
111+ --log-throughput \
112+ --log-interval 1 \
113+ --save-interval 10000 \
114+ --eval-interval 10000 \
115+ --eval-iters 0 \
116+"
117+ 
118+torchrun $DISTRIBUTED_ARGS pretrain_gpt.py \
119+ --npu-deterministic \
120+ $GPT_ARGS \
121+ $MOE_ARGS \
122+ $DATA_ARGS \
123+ $OUTPUT_ARGS \
124+ $DUALPIPE_ARGS \
125+ 2>&1 | tee -i base-deter-nofuse.log
126+ 
@@ -0,0 +1,166 @@
1+import pytest
2+import unittest
3+from unittest.mock import patch, MagicMock
4+ 
5+import torch
6+import torch_npu
7+ 
8+from mindspeed import megatron_adaptor
9+from mindspeed.core.pipeline_parallel.fb_overlap.modules.utils import (
10+ TensorSwapManager,
11+ make_wait_swap_in_hook,
12+ make_async_swap_in_hook
13+ )
14+ 
15+ 
16+class TestTensorSwapManager:
17+ 
18+ @classmethod
19+ def setup_class(cls):
20+ # Ensure we have a device to test with
21+ if not torch_npu.npu.is_available():
22+ pytest.skip("NPU device not available")
23+ 
24+ def test_initialization(self):
25+ """Test basic initialization of TensorSwapManager."""
26+ test_tensor = torch.randn(5, 5, device='npu')
27+ manager = TensorSwapManager(test_tensor)
28+
29+ assert manager.npu_tensor is test_tensor
30+ assert manager.cpu_tensor is None
31+ assert manager.swap_out_event is None
32+ assert manager.swap_in_event is None
33+ assert not manager.under_swap_in
34+ 
35+ def test_swap_group_initialization(self):
36+ """Test initialization with swap groups."""
37+ test_tensor = torch.randn(5, 5, device='npu')
38+ group_name = "test_group"
39+
40+ # First manager should create the group
41+ manager1 = TensorSwapManager(test_tensor, group_name)
42+ assert group_name in TensorSwapManager._ALL_SWAP_OUT_QUEUES
43+ assert len(TensorSwapManager._ALL_SWAP_OUT_QUEUES[group_name]) == 0
44+
45+ # Second manager should use existing group
46+ manager2 = TensorSwapManager(test_tensor, group_name)
47+ assert len(TensorSwapManager._ALL_SWAP_OUT_QUEUES[group_name]) == 0
48+ 
49+ def test_async_swap_out(self):
50+ """Test asynchronous swap-out functionality."""
51+ test_tensor = torch.randn(5, 5, device='npu')
52+ manager = TensorSwapManager(test_tensor)
53+
54+ # Perform async swap-out
55+ manager.async_swap_out()
56+
57+ # Verify swap-out was initiated
58+ assert manager.cpu_tensor is not None
59+ assert manager.swap_out_event is not None
60+ assert manager.cpu_tensor.device.type == 'cpu'
61+ assert manager.cpu_tensor.is_pinned()
62+ 
63+ def test_async_swap_out_wait_conditions(self):
64+ """Test swap-out with wait conditions."""
65+ test_tensor = torch.randn(5, 5, device='npu')
66+ manager = TensorSwapManager(test_tensor)
67+
68+ # Create dummy event and stream
69+ dummy_event = torch.npu.Event()
70+ dummy_event.record()
71+ dummy_stream = torch.npu.Stream()
72+
73+ # Should complete without errors
74+ manager.async_swap_out(wait_event=dummy_event, wait_stream=dummy_stream)
75+ 
76+ def test_wait_swap_out(self):
77+ """Test waiting for swap-out completion."""
78+ test_tensor = torch.randn(5, 5, device='npu')
79+ manager = TensorSwapManager(test_tensor)
80+
81+ manager.async_swap_out()
82+ manager.wait_swap_out()
83+
84+ # Verify NPU storage was released
85+ assert manager.npu_tensor.untyped_storage().size() == 0
86+ 
87+ def test_async_swap_in(self):
88+ """Test asynchronous swap-in functionality."""
89+ test_tensor = torch.randn(5, 5, device='npu')
90+ manager = TensorSwapManager(test_tensor)
91+
92+ # First swap out
93+ manager.async_swap_out()
94+ manager.wait_swap_out()
95+
96+ # Then swap back in
97+ manager.async_swap_in()
98+
99+ # Verify swap-in was initiated
100+ assert manager.swap_in_event is not None
101+ assert manager.under_swap_in
102+ 
103+ def test_wait_swap_in(self):
104+ """Test waiting for swap-in completion."""
105+ test_tensor = torch.randn(5, 5, device='npu')
106+ manager = TensorSwapManager(test_tensor)
107+
108+ # Full swap cycle
109+ manager.async_swap_out()
110+ manager.wait_swap_out()
111+ manager.async_swap_in()
112+ manager.wait_swap_in()
113+
114+ # Verify state after swap-in
115+ assert manager.cpu_tensor is None
116+ assert not manager.under_swap_in
117+ assert manager.npu_tensor.untyped_storage().size() > 0
118+ 
119+ def test_wait_all_swap_out(self):
120+ """Test waiting for all swaps in a group."""
121+ test_tensor = torch.randn(5, 5, device='npu')
122+ group_name = "group_test"
123+
124+ # Create two managers in the same group
125+ manager1 = TensorSwapManager(test_tensor, group_name)
126+ manager2 = TensorSwapManager(test_tensor.clone(), group_name)
127+
128+ # Initiate swaps
129+ manager1.async_swap_out()
130+ manager2.async_swap_out()
131+
132+ # Wait for all in group
133+ TensorSwapManager.wait_all_swap_out(group_name)
134+
135+ # Verify both were processed
136+ assert manager1.npu_tensor.untyped_storage().size() == 0
137+ assert manager2.npu_tensor.untyped_storage().size() == 0
138+ assert len(TensorSwapManager._ALL_SWAP_OUT_QUEUES[group_name]) == 0
139+ 
140+ def test_hook_functions(self):
141+ """Test the hook creation functions."""
142+ test_tensor = torch.randn(5, 5, device='npu')
143+ manager = TensorSwapManager(test_tensor)
144+
145+ # Test wait swap-in hook
146+ wait_hook = make_wait_swap_in_hook(manager)
147+ manager.async_swap_out()
148+ manager.wait_swap_out()
149+ manager.async_swap_in()
150+ wait_hook() # Should wait for swap-in
151+
152+ # Test async swap-in hook
153+ managers = [TensorSwapManager(test_tensor.clone()) for _ in range(3)]
154+ async_hook = make_async_swap_in_hook(managers)
155+
156+ # First swap out all
157+ for m in managers:
158+ m.async_swap_out()
159+ m.wait_swap_out()
160+
161+ # Trigger swap-in
162+ async_hook()
163+
164+ # Verify all initiated swap-in
165+ for m in managers:
166+ assert m.swap_in_event is not None