Skip to content

MoE Overlap: Add 2 CUDA Events to Synchronize Computation & Communication Operations for Forward and Backward Respectively - #2630

Closed
yanminjia wants to merge 1 commit into
NVIDIA:mainfrom
yanminjia:moe_overlap
Closed

MoE Overlap: Add 2 CUDA Events to Synchronize Computation & Communication Operations for Forward and Backward Respectively#2630
yanminjia wants to merge 1 commit into
NVIDIA:mainfrom
yanminjia:moe_overlap

Conversation

@yanminjia

@yanminjia yanminjia commented Dec 11, 2025

Copy link
Copy Markdown

Problem Description

Please refer to issue #2180. MoE communication & computation cannot overlap completely.

Root Cause Analysis

Basically, forward computation of a micro-batch overlaps with backward communication of a different micro-batch and vice versa during interleaving phase. Therefore, the forward or backward process with respect to a transformer layer is split into a couple of communication and computation modules which are managed by TransformerLayerSchedulePlan (megatron/core/model/common/mode_trunk_schedule_plan.py). Additionally, based on TransformerLayerSchedulePlan, the schedule plan of a model trunk is generated by TransformerModelChunkSchedulePlan (megatron/core/model/common/mode_trunk_schedule_plan.py).

Roughly, in the interleaving phase, a forward process of a micro-batch over a transformer layer moves forward side by side with a backward process of a different micro-batch over a different transformer layer within a model trunk. For example, backward combine (communication) is scheduled to go in parallel with forward attention (computation) on different CUDA streams, forward dispatch (communication) is conducted wth backward mlp.

To synchronize the operations on communication stream and computation stream, one CUDA event is used to manage the dependencies of the sub modules in forward pass or backward pass. The computation and communication in forward pass are independent of the computation and communication in backward pass and vice versa. With only one CUDA event for synchronization, a computation operation in forward pass may wait for a communication operation in backward pass mistakenly.

Solution

Add 2 CUDA events for communication & computation synchronization with regard to forward pass and backward pass respectively. A CUDA event is used for forward synchronization and a different CUDA is used for backward synchronization.

Test

As shown by below screen shot, forward attention overlaps with moe_combine backward.

5e060f2c-324f-4c87-addc-3418d7099226

… forward and backward pass respectively in case of MoE overlap
@yanminjia
yanminjia requested review from a team as code owners December 11, 2025 12:49
@copy-pr-bot

copy-pr-bot Bot commented Dec 11, 2025

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@wujiahao15

wujiahao15 commented Dec 15, 2025

Copy link
Copy Markdown
  • Taking the execution of post_attn_fwd, mlp_bwd, and moe_dispatch_fwd in MoE as an example, the sequence of CPU-side calls is as follows:
sequenceDiagram
    participant Host as Host (CPU)
    participant S1 as Stream 1
    participant S2 as Stream 2
    participant E as Event (e)

    Note over Host, S1: 1. cudaStreamWaitEvent(s1, e) <br/>(wait for the former event)
    Host->>S1: enqueue cudaStreamWaitEvent

    Note over Host, S1: 2. launch(post_attn_fwd)
    Host->>S1: Async Launch: post_attn_fwd
    activate S1

    Note over Host, S1: 3. cudaEventRecord(e, s1)
    Host->>S1: Record event e
    S1-->>E: (Event e is triggered after kernel completion)

    Note over Host, S1: 4. cudaStreamWaitEvent(s1, e)<br/>(Same-stream wait, no actual blocking)
    Host->>S1: enqueue cudaStreamWaitEvent

    Note over Host, S1: 5. launch(mlp_bwd)
    Host->>S1: Async Launch: mlp_bwd
    deactivate S1
    activate S1

    Note over Host, S1: 6. cudaEventRecord(e, s1)<br/>(The trigger point of e is updated to here)
    Host->>S1: Record Event e(overwriting the previous event)
    S1-->>E: (Event e is triggered after kernel completion)
    deactivate S1

    Note over Host, S2: 7. cudaStreamWaitEvent(s2, e)<br/>(Critical synchronization point)
    Host->>S2: enqueue cudaStreamWaitEvent
    E-->>S2: Block S2 until mlp_bwd completes

    Note over Host, S2: 8. launch(moe_dispatch_fwd)
    Host->>S2: Sync Launch: moe_dispatch_fwd (alltoall)
    activate S2

    Note over Host, S2: 9. cudaEventRecord(e, s2)
    Host->>S2: Record event e
    deactivate S2
Loading
  • When executed on the GPU, the actual execution order of the above operations is as follows:
gantt
    dateFormat X
    axisFormat %s
    title GPU Kernel Execution Timeline

    section Stream 1
    post_attn_fwd       :a1, 0, 10s
    Event Record (e)    :milestone, after a1
    mlp_bwd             :a2, after a1, 10s
    Event Record (e)    :crit, milestone, after a2

    section Stream 2
    Wait for Event (e)  :active, after a2, 0s
    moe_dispatch_fwd    :b1, after a2, 10s
    Event Record (e)    :milestone, after b1
Loading
  • The expected execution order on the GPU side is shown below:
gantt
    dateFormat X
    axisFormat %s
    title Expected result: moe_dispatch_fwd depends only on post_attn_fwd

    section Stream 1
    post_attn_fwd (k1)       :done, a1, 0, 10s
    Event Record (e)         :crit, milestone, after a1
    mlp_bwd (k2)             :active, a2, after a1, 10s

    section Stream 2
    Wait for Event (e)       :milestone, after a1, 0s
    moe_dispatch_fwd (k3)    :active, b1, after a1, 10s
    
    %% This row is only for visually emphasizing parallelism, not actual timing
    section Parallel State
    Overlap of S1 and S2     :crit, after a1, 10s
Loading
  • In my opinion, splitting a single CUDA event into two independent events for the forward and backward phases can resolve this issue.

@Wohox

Wohox commented Dec 15, 2025

Copy link
Copy Markdown
Contributor

@wujiahao15 Current design for ep overlap assigns 1 CUDA event for each microbatch and the overlapping always happen between 2 different microbatches, which is 1 event for fwd microbatch and 1 event for bwd microbatch.
Correct me if I misunderstand, your proposal seems to assume TransformerModelChunkSchedulePlan contains both forward and backward microbatches, but in the build method, it actually contains code for forward only, the backward reuses the event and this is necessary due to data dependency. Note that the run method is a classmethod, it takes 2 schedule plan as inputs, therefore forming what I mentioned earlier: 1 event for fwd microbatch and 1 event for bwd microbatch

@wujiahao15

wujiahao15 commented Dec 17, 2025

Copy link
Copy Markdown

@Wohox Thanks for pointing this out. You are correct and I misunderstood the TransformerModelChunkSchedulePlan design. With that corrected understanding, I have a follow-up question:

  • We use mixtra7B model and 8 H100 to run the EP Overlap, but we observe that both moe_dispatch fwd and moe_combine fwd can not overlap with the preceding or subsequent backward computation. We wonder why this problem exists.

  • moe_dispatch fwd
    img_v3_02t1_59448514-830d-48b0-ab0c-241989b7a96g

  • moe_combine fwd
    img_v3_02t2_552804ad-7e6b-4190-9fdc-7dac4d52d89g

@Wohox

Wohox commented Dec 17, 2025

Copy link
Copy Markdown
Contributor

@wujiahao15 Can you share the nsys timeline file, it would be easier for me to get information~
Besides, you can refer to this reply here and do a self check (#2180 (comment)).

@wujiahao15

Copy link
Copy Markdown

@Wohox Thanks for your timely reply. Here is the nsys-rep file. I will also refer to that benchmark repository and do self checks.

@yanminjia

yanminjia commented Dec 17, 2025

Copy link
Copy Markdown
Author

@Wohox @wujiahao15 Hello guys, many thanks for your kindly response. We are trying to maximize perforamance of MoE training & inference. Basically, if MoE computation & communication overlap is secured on the megatron side, we will refine NCCL AlltoAll collective communication based on mechanism such as sm-free & symmetric memory to save SM resource on the one side and reduce communication latency on the other side. Anyway, I suggest we could have an on-line or even off-line (if possible) meeting to discuss some kind of technology details. Many thanks.

@Wohox

Wohox commented Dec 17, 2025

Copy link
Copy Markdown
Contributor

@Wohox Thanks for your timely reply. Here is the nsys-rep file. I will also refer to that benchmark repository and do self checks.

2 issues I found

  • You can try enable --delay-wgrad-compute, this will help overlap dispatch_forward and combine_forward with wgrad.
  • Since you are using Mixtral7B, I would say the EP comm time is too small(200us). EP overlap normally works well when you have cross node EP settings where EP>8 if you are using Hopper.

@yanminjia

yanminjia commented Dec 18, 2025

Copy link
Copy Markdown
Author

Hello @Wohox, I tried Mixtral-8x7B with below MoE overlap settings:

  • --overlap-moe-expert-parallel-comm
  • --delay-wgrad-compute

Unfortunately, print error message as follows. Please refer to the configuration as attached.

mixtral7x8.sh

Would be highly appreciated if any clue. Many thanks.

[2025-12-18 20:27:25.458] [rank28]:     super().backward_dw()
[2025-12-18 20:27:25.458] [rank28]:   File "/inspire/hdd/project/h200-test-20251010/liuhe-W25090/yanmin/Megatron-main-org/megatron/core/extensions/transformer_engine.py", line 1391, in backward_dw
[2025-12-18 20:27:25.458] [rank28]:     self.linear_fc1.backward_dw()
[2025-12-18 20:27:25.458] [rank28]:   File "/inspire/hdd/project/h200-test-20251010/liuhe-W25090/yanmin/Megatron-main-org/megatron/core/transformer/moe/experts.py", line 1011, in backward_dw
[2025-12-18 20:27:25.458] [rank28]:     self.experts.backward_dw()
[2025-12-18 20:27:25.458] [rank28]:   File "/inspire/hdd/project/h200-test-20251010/liuhe-W25090/yanmin/Megatron-main-org/megatron/core/transformer/moe/moe_layer.py", line 298, in backward_dw
[2025-12-18 20:27:25.458] [rank28]:     module.backward_dw()
[2025-12-18 20:27:25.458] [rank28]:   File "/inspire/hdd/project/h200-test-20251010/liuhe-W25090/yanmin/Megatron-main-org/megatron/core/models/gpt/fine_grained_callables.py", line 288, in backward_dw
[2025-12-18 20:27:25.458] [rank28]:     b_layer.mlp.backward_dw()
[2025-12-18 20:27:25.458] [rank28]:   File "/inspire/hdd/project/h200-test-20251010/liuhe-W25090/yanmin/Megatron-main-org/megatron/core/models/common/model_chunk_schedule_plan.py", line 215, in run
[2025-12-18 20:27:25.458] [rank28]:                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[2025-12-18 20:27:25.458] [rank28]:     f_input, b_grad = TransformerLayerSchedulePlan.run(
[2025-12-18 20:27:25.458] [rank28]:   File "/inspire/hdd/project/h200-test-20251010/liuhe-W25090/yanmin/Megatron-main-org/megatron/core/models/common/model_chunk_schedule_plan.py", line 450, in run
[2025-12-18 20:27:25.458] [rank28]:                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[2025-12-18 20:27:25.458] [rank28]:     output_tensor = type(f_schedule_plan or b_schedule_plan).run(
[2025-12-18 20:27:25.458] [rank28]:   File "/inspire/hdd/project/h200-test-20251010/liuhe-W25090/yanmin/Megatron-main-org/megatron/core/pipeline_parallel/combined_1f1b.py", line 392, in combined_forward_backward_step
[2025-12-18 20:27:25.458] [rank28]:                                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[2025-12-18 20:27:25.458] [rank28]:     output_tensor, num_tokens, input_tensor_grad = combined_forward_backward_step(
[2025-12-18 20:27:25.458] [rank28]:   File "/inspire/hdd/project/h200-test-20251010/liuhe-W25090/yanmin/Megatron-main-org/megatron/core/pipeline_parallel/combined_1f1b.py", line 199, in combined_1f1b_schedule_for_interleaved_pipelining
[2025-12-18 20:27:25.458] [rank28]:            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[2025-12-18 20:27:25.458] [rank28]:     return combined_1f1b_schedule_for_interleaved_pipelining(
[2025-12-18 20:27:25.458] [rank28]:   File "/inspire/hdd/project/h200-test-20251010/liuhe-W25090/yanmin/Megatron-main-org/megatron/core/pipeline_parallel/schedules.py", line 1303, in forward_backward_helper_wrapper
[2025-12-18 20:27:25.458] [rank28]:                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[2025-12-18 20:27:25.458] [rank28]:     output_tensor, input_tensor_grad = forward_backward_helper_wrapper(
[2025-12-18 20:27:25.458] [rank28]:   File "/inspire/hdd/project/h200-test-20251010/liuhe-W25090/yanmin/Megatron-main-org/megatron/core/pipeline_parallel/schedules.py", line 1697, in forward_backward_pipelining_with_interleaving
[2025-12-18 20:27:25.458] [rank28]:                      ^^^^^^^^^^^^^^^^^^^^^^
[2025-12-18 20:27:25.458] [rank28]:     losses_reduced = forward_backward_func(
[2025-12-18 20:27:25.458] [rank28]:   File "/inspire/hdd/project/h200-test-20251010/liuhe-W25090/yanmin/Megatron-main-org/megatron/training/training.py", line 1257, in train_step
[2025-12-18 20:27:25.458] [rank28]:         ^^^^^^^^^^^
[2025-12-18 20:27:25.458] [rank28]:     ) = train_step(
[2025-12-18 20:27:25.458] [rank28]:   File "/inspire/hdd/project/h200-test-20251010/liuhe-W25090/yanmin/Megatron-main-org/megatron/training/training.py", line 2324, in train
[2025-12-18 20:27:25.458] [rank28]:                                                       ^^^^^^
[2025-12-18 20:27:25.458] [rank28]:     iteration, num_floating_point_operations_so_far = train(
[2025-12-18 20:27:25.458] [rank28]:   File "/inspire/hdd/project/h200-test-20251010/liuhe-W25090/yanmin/Megatron-main-org/megatron/training/training.py", line 737, in pretrain
[2025-12-18 20:27:25.458] [rank28]:     pretrain(
[2025-12-18 20:27:25.458] [rank28]:   File "/inspire/hdd/project/h200-test-20251010/liuhe-W25090/yanmin/Megatron-main-org/./pretrain_gpt.py", line 233, in <module>
[2025-12-18 20:27:25.458] [rank28]: Traceback (most recent call last):
[2025-12-18 20:27:25.458] [rank26]: RuntimeError: /TransformerEngine/transformer_engine/common/gemm/[cublaslt_gemm.cu:112](http://cublaslt_gemm.cu:112/) in function CanonicalizeGemmInput: Assertion failed: A.has_data() || A.has_columnwise_data(). Input A does not hold any data!
[2025-12-18 20:27:25.458] [rank26]:            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[2025-12-18 20:27:25.458] [rank26]:     bias = tex.te_general_grouped_gemm(
[2025-12-18 20:27:25.458] [rank26]:   File "/usr/local/lib/python3.12/dist-packages/transformer_engine/pytorch/cpp_extensions/gemm.py", line 208, in general_grouped_gemm
[2025-12-18 20:27:25.458] [rank26]:            ^^^^^^^^^^^^^^^^^^
[2025-12-18 20:27:25.458] [rank26]:     return func(*tensor_list), tensor_list
[2025-12-18 20:27:25.458] [rank26]:   File "/usr/local/lib/python3.12/dist-packages/transformer_engine/pytorch/module/_common.py", line 277, in pop
[2025-12-18 20:27:25.458] [rank26]:                                         ^^^^^^^^^^^^^^^^^^^^^^
[2025-12-18 20:27:25.458] [rank26]:     (_, grad_biases_, _), tensor_list = self.wgrad_store.pop()
[2025-12-18 20:27:25.458] [rank26]:   File "/usr/local/lib/python3.12/dist-packages/transformer_engine/pytorch/module/grouped_linear.py", line 823, in backward_dw
[2025-12-18 20:27:25.458] [rank26]:     super().backward_dw()
[2025-12-18 20:27:25.458] [rank26]:   File "/inspire/hdd/project/h200-test-20251010/liuhe-W25090/yanmin/Megatron-main-org/megatron/core/extensions/transformer_engine.py", line 1391, in backward_dw
[2025-12-18 20:27:22.235] NCCL version 2.27.3+cuda12.9
[2025-12-18 20:27:22.235] NCCL version 2.27.3+cuda12.9

By the way, with the attached configuration, TFLOPs can go up to 490+ even if no MoE overlap enabled.

@Wohox

Wohox commented Jan 23, 2026

Copy link
Copy Markdown
Contributor

@yanminjia It seems this error is because of local expert number being 1 (EP8 & num_experts=8), you should be able to resolve the issue by either of these WARs:

The official fix should be soon, thanks~

@chtruong814 chtruong814 removed the needs-follow-up Issue needs follow-up label Jan 23, 2026
@Wohox

Wohox commented Feb 2, 2026

Copy link
Copy Markdown
Contributor

@yanminjia update: fix PR - #3163

@Phlip79

Phlip79 commented Mar 4, 2026

Copy link
Copy Markdown
Member

Fixed by #3164.

@Phlip79 Phlip79 closed this Mar 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants