Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
228c35f
Fix mHC boundaries in EP overlap schedule
jingqiny-99 Jun 24, 2026
47f91ef
Address mHC EP overlap review comments
jingqiny-99 Jun 24, 2026
6c2f9e1
Address mHC EP overlap follow-up review
jingqiny-99 Jun 25, 2026
53d9070
Add configurable mHC overlap scheduling
jingqiny-99 Jul 3, 2026
3835ced
Replace mHC profiling artifacts with a schedule timeline
jingqiny-99 Jul 8, 2026
bd2d3e3
Remove mHC high-priority stream scheduling
jingqiny-99 Jul 8, 2026
2238278
Add compute-stream scheduling for mHC post
jingqiny-99 Jul 8, 2026
8dcd316
Remove unused mHC recompute group bounds
jingqiny-99 Jul 8, 2026
b40acc0
Fix mHC boundary and HybridMoE config tests
jingqiny-99 Jul 10, 2026
245cdea
Fix: docstring
jingqiny-99 Jul 10, 2026
b285936
Delete docs/images/mhc_overlap/mhc_ep_overlap_partial_cuda_graph_time…
jingqiny-99 Jul 14, 2026
dc54cc2
Finalize mHC decoder boundary at the terminal schedule node
jingqiny-99 Jul 15, 2026
8dcc183
Restore activation dtype for dense mHC MLP; relax interleaved MTP gra…
jingqiny-99 Jul 15, 2026
93d03c1
Fix dense mHC MLP dtype cast to target params_dtype
jingqiny-99 Jul 15, 2026
d2b9c9f
Remove mhc_post_on_compute_stream flag and the separate mHC-post node…
jingqiny-99 Jul 15, 2026
a9c6898
upd: fix lint
jingqiny-99 Jul 15, 2026
f5cc9c9
Fix MTP delayed-wgrad callable selection under hyper-connections
jingqiny-99 Jul 16, 2026
054ec0f
Extend mHC EP-overlap tests: delayed wgrad, MTP recompute, full-itera…
jingqiny-99 Jul 16, 2026
b0c506f
Set params_dtype in PP mHC forward test config
jingqiny-99 Jul 16, 2026
35206e8
Flush CSA and DSA deferred weight gradients through core_attention
jingqiny-99 Jul 17, 2026
317727d
Add delayed-wgrad flush tests for CSA and DSA attention variants
jingqiny-99 Jul 17, 2026
2a8f4a7
Disambiguate mHC recompute NVTX labels and trim a stale comment
jingqiny-99 Jul 17, 2026
dd14dc7
Merge remote-tracking branch 'origin/dev' into jingqiny/fix-mhc-ep-ov…
jingqiny-99 Jul 21, 2026
db98830
Merge branch 'dev' into jingqiny/fix-mhc-ep-overlap-dev
jingqiny-99 Jul 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 69 additions & 9 deletions megatron/core/models/common/model_chunk_schedule_plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from megatron.core.pipeline_parallel.utils import (
AbstractSchedulePlan,
NoopScheduleNode,
ScheduleNode,
get_comm_stream,
get_comp_stream,
)
Expand All @@ -28,17 +29,17 @@ class ModelChunkState:


class TransformerLayerSchedulePlan:
"""Schedule the executing plan of the nodes in a transformer/mtp layer.
"""Schedule the execution plan for nodes in a transformer or MTP layer.

This class organizes the sub-modules of a transformer/mtp layer,
including attention, post attention, MLP, dispatch, combine and
mtp post process nodes.
This class organizes the submodules of a transformer or MTP layer, including attention,
MLP, MoE dispatch and combine, optional mHC recomputation, and MTP post-processing nodes.

layer (TransformerLayerSchedulePlan)
├── attn (TransformerLayerNode): attention -> layernorm -> router -> dispatch preprocess
├── moe_dispatch (TransformerLayerNode): dispatch All2All
├── mlp (TransformerLayerNode): mlp module
├── moe_combine (TransformerLayerNode): combine All2All
├── moe_combine (TransformerLayerNode): combine All2All (incl. MLP-side mHC post-processing)
├── mhc_recompute (ScheduleNode): optional explicit replay before mHC backward
└── mtp_post_process (PostProcessNode): mtp post process

Note that MTP layer has the same operation and execution order with TransformerLayer regarding
Expand All @@ -52,6 +53,7 @@ class TransformerLayerSchedulePlan:
moe_dispatch = None
mlp = None
moe_combine = None
mhc_recompute = None
mtp_post_process = None

def __init__(self, layer, event, chunk_state, comp_stream, comm_stream, extra_args={}):
Expand Down Expand Up @@ -97,6 +99,9 @@ def release_state(self):
if hasattr(self, 'moe_combine') and self.moe_combine is not None:
del self.moe_combine
self.moe_combine = None
if hasattr(self, 'mhc_recompute') and self.mhc_recompute is not None:
del self.mhc_recompute
self.mhc_recompute = None
if hasattr(self, 'mtp_post_process') and self.mtp_post_process is not None:
del self.mtp_post_process
self.mtp_post_process = None
Expand All @@ -109,7 +114,7 @@ def release_state(self):
def _build_callable_nodes(self, event, comp_stream, comm_stream, extra_args):
"""
Builds the callable nodes for the transformer/mtp layer:
attn, mlp, moe_dispatch and moe_combine, and mtp_post_process.
attn, mlp, moe_dispatch, moe_combine, and mtp_post_process.
"""
from megatron.core.models.gpt.fine_grained_callables import (
TransformerLayerNode,
Expand Down Expand Up @@ -166,6 +171,24 @@ def create_node(stream, module, name):
self.moe_dispatch = NoopScheduleNode()
self.moe_combine = NoopScheduleNode()

mhc_recompute_manager = extra_args.get("mhc_recompute_manager")
if mhc_recompute_manager is not None and extra_args.get(
"is_last_layer_in_mhc_recompute_group", False
):
group_index = extra_args["mhc_recompute_group_index"]
# The group counter restarts per module (decoder / mtp), so fold the
# module tag into the NVTX label to keep profiles unambiguous.
module_tag = extra_args.get("mhc_recompute_module_tag", "decoder")
self.mhc_recompute = ScheduleNode(
mhc_recompute_manager.recompute_now,
comp_stream,
event,
name="mhc_recompute",
forward_nvtx_name=f"mhc/recompute/{module_tag}/group_{group_index}/B",
)
else:
self.mhc_recompute = None

if is_mtp:
self.mtp_post_process = create_node(
comp_stream, mtp_post_process_module, "mtp_post_process"
Expand Down Expand Up @@ -237,6 +260,9 @@ def run(f_layer, b_layer, f_input=None, b_grad=None, is_last_layer_in_bwd=False)
When f_layer and b_layer are not None, forward and backward pass are overlapped as follows:
comm_stream: combine_bwd | dispatch_fwd->dispatch_bwd | combine_fwd
comp_stream: attn_fwd | mlp_bwd->mlp_bwd_dw->mlp_fwd| attn_bwd
MLP-side mHC post-processing runs inside the combine node on the communication stream.
Group recompute runs on the normal compute stream immediately before the node containing
mHC post-processing backward.
For MTP, mtp_post_process_fwd is executed after the combine_fwd in the comp_stream,
and mtp_post_process_bwd is executed before the combine_bwd in the comp_stream.

Expand All @@ -254,6 +280,8 @@ def run(f_layer, b_layer, f_input=None, b_grad=None, is_last_layer_in_bwd=False)

if b_layer is not None:
b_grad = b_layer.mtp_post_process.backward(b_grad)
if b_layer.mhc_recompute is not None:
b_layer.mhc_recompute.forward()
b_grad = b_layer.moe_combine.backward(b_grad)

if f_layer is not None:
Expand Down Expand Up @@ -369,6 +397,7 @@ def __init__(
self._model_chunk_state.decoder_input = decoder_input
self._model_chunk_state.labels = labels
self._model_chunk_state.mtp_hidden_states = None
self._model_chunk_state.mhc_multistream = None
self._model_chunk_state.loss_mask = loss_mask
self._model_chunk_state.packed_seq_params = packed_seq_params
self._model_chunk_state.padding_mask = padding_mask
Expand All @@ -389,9 +418,11 @@ def __init__(
# build layer schedule plan for each layer.
# The methods to obtain layers are different for MTP so we need the other build plan for
# MTP. Also, this can help annotate MTP layer so that it can know where MTP is.
self._build_layer_schedule_plan(model.decoder, get_comp_stream, get_comm_stream)
self._build_layer_schedule_plan(
getattr(model, "mtp", None), get_comp_stream, get_comm_stream
model.decoder, get_comp_stream, get_comm_stream, module_tag="decoder"
)
self._build_layer_schedule_plan(
getattr(model, "mtp", None), get_comp_stream, get_comm_stream, module_tag="mtp"
)

# build post process
Expand All @@ -400,14 +431,39 @@ def __init__(
model, self._model_chunk_state, self._event, get_comp_stream
)

def _build_layer_schedule_plan(self, module, comp_stream, comm_stream):
def _build_layer_schedule_plan(self, module, comp_stream, comm_stream, module_tag):
if module is None:
return

from megatron.core.tensor_parallel.random import CheckpointManager

num_layers = len(module.layers)
config = module.config
use_mhc_recompute = (
module.training
and torch.is_grad_enabled()
and config.enable_hyper_connections
and config.recompute_granularity == "selective"
and "mhc" in config.recompute_modules
)
group_size = config.mhc_recompute_layer_num or num_layers
mhc_recompute_manager = (
CheckpointManager() if use_mhc_recompute and num_layers > 0 else None
)
group_index = 0

for layer_idx in range(num_layers):
is_group_end = bool(
mhc_recompute_manager is not None
and (layer_idx == num_layers - 1 or (layer_idx + 1) % group_size == 0)
)
extra_args = {
"is_first_layer": layer_idx == 0,
"is_last_layer": layer_idx == num_layers - 1,
"mhc_recompute_manager": mhc_recompute_manager,
"is_last_layer_in_mhc_recompute_group": is_group_end,
"mhc_recompute_group_index": group_index,
"mhc_recompute_module_tag": module_tag,
}
layer_plan = TransformerLayerSchedulePlan(
module.layers[layer_idx],
Expand All @@ -419,6 +475,10 @@ def _build_layer_schedule_plan(self, module, comp_stream, comm_stream):
)
self._transformer_layers.append(layer_plan)

if is_group_end and layer_idx != num_layers - 1:
group_index += 1
mhc_recompute_manager = CheckpointManager()

@property
def event(self):
"""Gets the CUDA event for synchronization."""
Expand Down
Loading
Loading