Skip to content

Commit 56b5c88

Browse files
Merge pull request #102 from lyzxs007/feature/moe-ep-load-balance
[vlm] fix: fix metadata parameter passing in the EP overlap path
2 parents 46cc7a3 + 7beb6e0 commit 56b5c88

2 files changed

Lines changed: 56 additions & 26 deletions

File tree

loongforge/models/omni_models/fine_grained_callables.py

Lines changed: 47 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,15 @@ def wrapped_func(*args, **kwarg):
6161
return wrapped_func
6262

6363

64-
def should_free_input(name, is_moe, is_deepep):
64+
def should_free_input(name, is_moe, enable_deepep, enable_hybridep):
6565
"""Determine if the node should free its input memory.
6666
6767
Args:
6868
name: Node name
6969
is_moe: Whether it's a MoE model
70-
is_deepep: Whether it's a DeepEP model
71-
70+
enable_deepep: Whether to use DeepEP dispatcher
71+
enable_hybridep: Whether to use HybridEP dispatcher
72+
7273
Returns:
7374
bool: Whether to free input memory
7475
"""
@@ -81,13 +82,13 @@ def should_free_input(name, is_moe, is_deepep):
8182
# The input and output of A2A are not needed anymore after the forward pass,
8283
# so we can free the input memory after the forward pass.
8384
free_input_nodes = {
84-
"mlp": True,
85+
"mlp": not enable_hybridep,
8586
"moe_combine": True,
86-
"post_combine": is_deepep,
87+
"post_combine": enable_deepep or enable_hybridep,
8788
# For non-deepep mode, the input is the un-dispatched tokens and probs before dispatch A2A
8889
# and it's not needed anymore after the forward pass
8990
# For deepep mode, they are both needed in backward pass, so they cannot be freed.
90-
"moe_dispatch": not is_deepep,
91+
"moe_dispatch": not (enable_deepep or enable_hybridep),
9192
}
9293

9394
return free_input_nodes.get(name, False)
@@ -625,12 +626,13 @@ def __init__(
625626
it's the per_batch_state_context, o.w. nullcontext
626627
name (str): Node name, also used to determine memory strategy
627628
bwd_dw_callables (list): List of weight gradient functions for the layer.
628-
extra_args (dict): Extra arguments for the node: is_moe, enable_deepep.
629+
extra_args (dict): Extra arguments for nodes: is_moe, enable_deepep, enable_hybridep.
629630
"""
630631
# determine whether to free input memory
631632
is_moe = extra_args.get("is_moe", False)
632633
enable_deepep = extra_args.get("enable_deepep", False)
633-
free_input = should_free_input(name, is_moe, enable_deepep)
634+
enable_hybridep = extra_args.get("enable_hybridep", False)
635+
free_input = should_free_input(name, is_moe, enable_deepep, enable_hybridep)
634636
self.delay_wgrad_compute = extra_args.get("delay_wgrad_compute", False)
635637
self.layer_idx = extra_args.get("layer_idx", None)
636638

@@ -727,7 +729,14 @@ def build_transformer_layer_callables(layer: TransformerLayer):
727729
"""
728730

729731
is_moe = isinstance(layer.mlp, MoELayer)
730-
enable_deepep = layer.config.moe_enable_deepep
732+
enable_deepep = (
733+
layer.config.moe_token_dispatcher_type == "flex"
734+
and layer.config.moe_flex_dispatcher_backend == "deepep"
735+
)
736+
enable_hybridep = (
737+
layer.config.moe_token_dispatcher_type == "flex"
738+
and layer.config.moe_flex_dispatcher_backend == "hybridep"
739+
)
731740
is_alltoall_dispatcher = (
732741
is_moe and layer.config.moe_token_dispatcher_type == "alltoall"
733742
)
@@ -782,13 +791,17 @@ def submodule_post_attn_forward(node: ScheduleNode, hidden_states: torch.Tensor)
782791
pre mlp layernorm->router->dispatch preprocess
783792
"""
784793
if layer.a2a_overlap_post_attn_recompute:
794+
metadata_holder = {}
785795
def custom_forward(hidden_states):
786796
pre_mlp_layernorm_output = layer.pre_mlp_layernorm(hidden_states)
787-
local_tokens, probs, _ = layer.mlp.router_and_preprocess(pre_mlp_layernorm_output)
788-
return pre_mlp_layernorm_output, local_tokens, probs
797+
local_tokens, probs, metadata_holder['metadata'], _ = (
798+
layer.mlp.router_and_preprocess(pre_mlp_layernorm_output)
799+
)
800+
return pre_mlp_layernorm_output, local_tokens, probs
789801

790802
pre_mlp_layernorm_output, local_tokens, probs = tensor_parallel.checkpoint(
791803
custom_forward, False, hidden_states)
804+
metadata = metadata_holder['metadata']
792805

793806
else:
794807
if layer.offload_mlp_norm:
@@ -803,26 +816,30 @@ def custom_forward(hidden_states):
803816
with get_fine_grained_offloading_context(layer.offload_mlp_norm):
804817
pre_mlp_layernorm_output = layer.pre_mlp_layernorm(hidden_states)
805818

806-
local_tokens, probs, _ = layer.mlp.router_and_preprocess(pre_mlp_layernorm_output)
819+
local_tokens, probs, metadata, _ = layer.mlp.router_and_preprocess(
820+
pre_mlp_layernorm_output
821+
)
822+
823+
node.layer_state.dispatch_metadata = metadata
807824

808825
# Save token_dispatcher attributes to per-microbatch layer_state to protect against
809826
# recompute corruption when f_layer == b_layer in combined 1F1B schedule
810827
# (occurs at the middle layer when chunk has odd number of layers).
811828
# These attributes are used in combine_postprocess for unpermute and view operations.
812-
node.layer_state.hidden_shape = layer.mlp.token_dispatcher.hidden_shape
829+
node.layer_state.hidden_shape = metadata.hidden_shape
813830
# hidden_shape_before_permute and reversed_local_input_permutation_mapping are only
814831
# used in AlltoAll dispatcher's combine_postprocess (unpermute). AllGather uses them
815832
# in combine_preprocess which runs before post_combine, and Flex uses a different
816833
# attribute (reversed_mapping_for_combine). So we only save them for alltoall.
817834
if is_alltoall_dispatcher:
818835
node.layer_state.hidden_shape_before_permute = (
819-
layer.mlp.token_dispatcher.hidden_shape_before_permute
836+
metadata.hidden_shape_before_permute
820837
)
821838
# reversed_local_input_permutation_mapping is used as indices in unpermute.
822839
# It's an index tensor that doesn't require grad, so we save it directly
823840
# without using node.detach() to avoid adding it to the backward graph.
824841
node.layer_state.reversed_local_input_permutation_mapping = (
825-
layer.mlp.token_dispatcher.reversed_local_input_permutation_mapping
842+
metadata.reversed_local_input_permutation_mapping
826843
)
827844

828845
# Detach here for mlp_bda residual connection
@@ -840,12 +857,13 @@ def submodule_dispatch_forward(
840857
Dispatches tokens to the experts based on the router output.
841858
"""
842859
token_dispatcher = layer.mlp.token_dispatcher
843-
if enable_deepep:
860+
if enable_deepep or enable_hybridep:
844861
# update token_probs to be the detached version, prevents
845862
# backward graph from connecting to attn submodule
846863
token_dispatcher._comm_manager.token_probs = probs
847864

848-
dispatched_tokens, dispatched_probs = layer.mlp.dispatch(local_tokens, probs)
865+
metadata = node.layer_state.dispatch_metadata
866+
dispatched_tokens, dispatched_probs = layer.mlp.dispatch(local_tokens, probs, metadata)
849867
node.layer_state.dispatched_probs = node.detach(dispatched_probs)
850868
return dispatched_tokens
851869

@@ -859,15 +877,16 @@ def submodule_moe_forward(
859877
shared_expert_output = None
860878
dispatched_probs = node.layer_state.dispatched_probs
861879
token_dispatcher = layer.mlp.token_dispatcher
862-
if enable_deepep:
880+
if enable_deepep or enable_hybridep:
863881
# update dispatched_probs to be detached version, prevents
864882
# backward graph from connecting to dispatch submodule
865883
token_dispatcher._comm_manager.dispatched_probs = dispatched_probs
866884

867885
pre_mlp_layernorm_output = getattr(node.layer_state, 'pre_mlp_layernorm_output', None)
886+
metadata = node.layer_state.dispatch_metadata
868887

869888
dispatched_input, tokens_per_expert, permuted_probs = layer.mlp.pre_routed_experts_compute(
870-
dispatched_tokens, dispatched_probs)
889+
dispatched_tokens, dispatched_probs, metadata)
871890

872891
if layer.a2a_overlap_mlp_recompute:
873892
def custom_forward(dispatched_input, tokens_per_expert, permuted_probs, pre_mlp_layernorm_output):
@@ -892,7 +911,7 @@ def custom_forward(dispatched_input, tokens_per_expert, permuted_probs, pre_mlp_
892911
dispatched_input, tokens_per_expert, permuted_probs
893912
)
894913

895-
expert_output = layer.mlp.post_routed_experts_compute(expert_output)
914+
expert_output = layer.mlp.post_routed_experts_compute(expert_output, metadata)
896915

897916
if layer.recompute_pre_mlp_layernorm:
898917
# discard the output of the pre-mlp layernorm and register the recompute
@@ -923,7 +942,8 @@ def submodule_combine_forward(
923942
Triggers token combine communication.
924943
This communication can be overlapped with computation from another microbatch.
925944
"""
926-
output = layer.mlp.combine(output)
945+
metadata = node.layer_state.dispatch_metadata
946+
output = layer.mlp.combine(output, metadata)
927947
return output
928948

929949
def submodule_post_combine_forward(
@@ -935,14 +955,15 @@ def submodule_post_combine_forward(
935955
"""
936956
residual = node.layer_state.residual
937957
shared_expert_output = getattr(node.layer_state, 'shared_expert_output', None)
958+
metadata = node.layer_state.dispatch_metadata
938959

939960
# Restore token_dispatcher attributes from per-microbatch layer_state before
940961
# combine_postprocess, to avoid corruption when backward recompute of another
941962
# microbatch overwrites token_dispatcher attributes (happens when f_layer == b_layer
942963
# in combined 1F1B).
943964
saved_hidden_shape = getattr(node.layer_state, 'hidden_shape', None)
944965
if saved_hidden_shape is not None:
945-
layer.mlp.token_dispatcher.hidden_shape = saved_hidden_shape
966+
metadata.hidden_shape = saved_hidden_shape
946967
# Only restore alltoall-specific attributes when using alltoall dispatcher.
947968
if is_alltoall_dispatcher:
948969
saved_hidden_shape_before_permute = getattr(
@@ -952,16 +973,16 @@ def submodule_post_combine_forward(
952973
node.layer_state, 'reversed_local_input_permutation_mapping', None
953974
)
954975
if saved_hidden_shape_before_permute is not None:
955-
layer.mlp.token_dispatcher.hidden_shape_before_permute = saved_hidden_shape_before_permute
976+
metadata.hidden_shape_before_permute = saved_hidden_shape_before_permute
956977
if saved_reversed_local_input_permutation_mapping is not None:
957-
layer.mlp.token_dispatcher.reversed_local_input_permutation_mapping = (
978+
metadata.reversed_local_input_permutation_mapping = (
958979
saved_reversed_local_input_permutation_mapping
959980
)
960981
# Release the index tensor reference early to allow GC before _release_state()
961982
node.layer_state.reversed_local_input_permutation_mapping = None
962983

963984
# Post-process combine and add shared expert output
964-
output = layer.mlp.post_combine(output, shared_expert_output)
985+
output = layer.mlp.post_combine(output, metadata, shared_expert_output)
965986
mlp_output_with_bias = (output, None)
966987

967988
with layer.bias_dropout_add_exec_handler():
@@ -984,6 +1005,7 @@ def submodule_post_combine_forward(
9841005
shared_expert_output.untyped_storage().resize_(0)
9851006
node.layer_state.residual = None
9861007
node.layer_state.shared_expert_output = None
1008+
node.layer_state.dispatch_metadata = None
9871009

9881010
# final layer norm from decoder
9891011
final_layernorm = node.chunk_state.model.foundation_model.decoder.final_layernorm

loongforge/models/omni_models/model_chunk_schedule_plan.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,17 @@ def _build_callable_nodes(self, event, comp_stream, comm_stream, extra_args):
119119
if is_mtp
120120
else isinstance(self.layer.mlp, MoELayer)
121121
)
122-
enable_deepep = self.layer.config.moe_enable_deepep
122+
enable_deepep = (
123+
self.layer.config.moe_token_dispatcher_type == "flex"
124+
and self.layer.config.moe_flex_dispatcher_backend == "deepep"
125+
)
126+
enable_hybridep = (
127+
self.layer.config.moe_token_dispatcher_type == "flex"
128+
and self.layer.config.moe_flex_dispatcher_backend == "hybridep"
129+
)
123130
deepstack_handler = extra_args.pop("deepstack_handler")
124131
extra_args["enable_deepep"] = enable_deepep
132+
extra_args["enable_hybridep"] = enable_hybridep
125133
extra_args["is_moe"] = is_moe
126134
extra_args["delay_wgrad_compute"] = self.layer.config.delay_wgrad_compute
127135
extra_args["is_mtp"] = is_mtp

0 commit comments

Comments
 (0)