Skip to content

[Feature] Integrate MoonEP dispatcher for FSDP expert-parallel training - #2056

Open
jayhenry wants to merge 15 commits into
InternLM:mainfrom
jayhenry:moonep
Open

[Feature] Integrate MoonEP dispatcher for FSDP expert-parallel training#2056
jayhenry wants to merge 15 commits into
InternLM:mainfrom
jayhenry:moonep

Conversation

@jayhenry

Copy link
Copy Markdown
Collaborator

Summary

This PR adds an optional dispatcher="moonep" path for node-local BF16 MoE training. Native FSDP2 remains the sole owner of expert parameters, optimizer state, and checkpoint identity; MoonEP owns only the communication/VMM execution workspace.

  • Add a model-scoped MoonEP runtime and adapt it to XTuner's six-stage dispatcher API.
  • Land FSDP all-gathered BF16 expert weights directly into MoonEP VMM aliases.
  • Dispatch activations and weights, run the existing grouped GEMM path, and return duplicated expert gradients in BF16 before FSDP reduce-scatter.
  • Support MTP (shared and unshared weights), Domino intra-layer micro-batching, sequence parallelism, activation recompute, and torch.compile.
  • Integrate DCP/HF persistence, activation offload, optimizer swap, Muon, and explicit runtime teardown.
  • Add a reproducible Qwen3.5 20-step DeepEP/MoonEP acceptance gate and detailed validation report.
  • Fix PyTorch 2.12 FSDP _StridedShard recognition in distributed grad-norm calculation.
flowchart LR
    A["FSDP all-gather: complete BF16 experts"] --> B["MoonEP VMM direct landing"]
    B --> C["Dispatch + grouped GEMM + combine"]
    C --> D["BF16 duplicated-gradient return"]
    D --> E["FSDP reduce-scatter"]
    E --> F["FP32 shard gradient + optimizer state"]
Loading

Design

MoonEP is imported lazily only when selected. The integration validates a versioned backend capability before allocating resources, so DeepEP, All2All, AGRS, and non-EP configurations do not acquire a MoonEP dependency.

The hot path is ordered with CUDA events and GPU-side EP barriers. The profiler regression gate verifies:

  • no complete home-expert weight copy in the direct path;
  • no complete local duplicated-gradient temporary;
  • no CUDA device/event/stream host synchronization between planning and duplicated-gradient handoff.

FSDP post-all-gather hooks install the VMM landing tensors while preserving the original DTensor parameters. The backward path returns BF16 home gradients to those parameter edges; existing FSDP communication then reduce-scatters them and produces FP32 sharded gradients for the FP32 optimizer update.

Supported scope

  • PyTorch 2.12.1+cu132.
  • Single-node BF16 EP2/EP4/EP8; formal end-to-end acceptance uses FSDP2 + EP4 on 8 GPUs.
  • TP1.
  • MTP, Domino micro-batching, sequence parallelism, activation recompute, and compile.
  • Synchronous/asynchronous DCP, HF export/load, AdamW, swap AdamW, and Muon.

Not claimed in this first version: Expert TP, FP8 experts, cross-node MoonEP, FSDP no_sync, pipeline parallelism, or decoding.

External MoonEP dependency

This XTuner branch expects MoonEP XTuner integration API v2. The validated companion implementation is currently commit c14bd43001efd8233950bb99e8eac9b1bafbdcc4 on the local MoonEP xtuner-integration branch.

The companion MoonEP patch still needs to be published separately before this PR can be merged or reproduced outside the development environment. The XTuner package does not add MoonEP as a mandatory dependency.

Validation

The formal workload is Qwen3.5-35B-A3B on one node with 8 H200 GPUs, FSDP2 + EP4, BF16 parameters/reduction, Direct VMM landing, Triton grouped GEMM, and 20 training steps. Each comparison changes only the dispatcher.

Gate DeepEP median tokens/s MoonEP median tokens/s MoonEP / DeepEP
MTP disabled, steps 6-20 5980.999 6047.073 101.10%
MTP1, steps 6-20 5357.164 5408.172 100.95%

For both gates, all loss and grad-norm curves have cosine similarity >= 0.99 and mean relative difference < 1%.

Additional GPU regressions:

  • MoonEP forward/MTP/Domino/SP/compile: 16 passed.
  • DCP/HF/offload/optimizer/lifecycle: 10 passed.
  • Dispatcher and grouped-GEMM regressions: passed.
  • FSDP2 + EP4 nested-shard grad norm: passed on PyTorch 2.12; compatibility regression also passed on PyTorch 2.9.
  • GLM FSDP2 + EP4 training, MoonEP/DeepEP two-step numerical parity, MTP reentrant micro2, and DCP cold resume: passed.

See the acceptance report for the full configuration, per-step metrics, profiler evidence, and reproduction commands.

Result

The first-version objective is met for single-node BF16 FSDP2 + EP training: MoonEP matches DeepEP numerically and reaches equivalent steady-state throughput while preserving FSDP parameter/checkpoint ownership and a host-sync-free communication hot path.

@jayhenry
jayhenry force-pushed the moonep branch 2 times, most recently from 06d5929 to 84b40b4 Compare August 31, 2026 09:49
@jayhenry

jayhenry commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator Author

@claude review

Comment thread xtuner/v1/module/dispatcher/moonep.py Outdated
Comment thread xtuner/v1/model/moe/moe.py Outdated
assert isinstance(loss_ctx, list) and len(loss_ctx) == len(seq_ctx), (
"seq_ctx_list and loss_ctx_list must be lists of the same length"
)
if self._moonep_runtime is not None and len(seq_ctx) > self.config.intra_layer_micro_batch:

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

改为通用检查 len(seq_ctx) == self.config.intra_layer_micro_batch

Comment thread xtuner/v1/module/dispatcher/moonep.py Outdated
staging_reference: bool,
num_sms: int = 64,
) -> None:
self._backend = require_moonep_backend()

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

直接检查是否安装 moonep,不做过度检查。
另外,在文件顶层 try import moonep 即可,后续引用也直接用 moonep,去掉 self._backend

Comment thread xtuner/v1/module/router/greedy.py Outdated
# moe forward
# (e, )
tokens_per_expert = torch.histc(topk_ids, bins=self.n_routed_experts, min=0, max=self.n_routed_experts)
tokens_per_expert = torch.bincount(topk_ids.flatten(), minlength=self.n_routed_experts)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

torch.bincount 存在强制 Host-Device 同步,改回原来的 torch.histc

n_routed_experts=n_routed_experts,
ep_group=process_group,
tp_group=tp_group,
ep_tp_group=ep_tp_group,

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

先hardcode bf16,去掉这个参数,assert 不支持 fp8

Comment thread xtuner/v1/module/router/noaux_router.py Outdated
min=0,
max=self.n_routed_experts,
) # .view(self.ep_mesh.size(), -1)
tokens_per_expert = torch.bincount(topk_ids.flatten(), minlength=self.n_routed_experts)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

同样要使用不host sync的方式,比如 histc

from torch import Tensor


try:

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

保留 try except 的import 方式

fsdp_root: nn.Module,
targets: Sequence[tuple[str, tuple[nn.Module, nn.Module], tuple[torch.Tensor, torch.Tensor]]],
) -> tuple[FSDPParam, ...]:
"""Bind routed expert FSDPParams to their two-generation VMM landings.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

将各种检查封装到一个私有函数,保证顶层的线性步骤

Comment thread xtuner/v1/module/dispatcher/moonep.py Outdated
self._fixed_tokens_per_rank: int | None = None
self._closed = False

def bind_dispatcher(

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

改名为 build_dispatcher 是否更合适?

Comment thread xtuner/v1/module/dispatcher/moonep.py Outdated
"""Own one fresh MoonEP plan and all of its device-side completion
edges."""

def __init__(self, runtime: MoonEPRuntime, *, layer_id: int, grad_slot: int) -> None:

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

将 runtime 整个传参是否参数过宽?

topk_ids,
tokens_per_expert,
topk_weights,
self,

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

将 invocation 整个传参是否参数过宽?

Comment thread xtuner/v1/module/dispatcher/moonep.py Outdated
# completed home gradients are handed back before FSDP post-backward.
with torch.profiler.record_function("MoonEP::prepare_experts"):
home_parameters = self._current_home_parameters()
local_weights, gradient_targets, weights_ready = workspace.materialize(

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个函数会 prefetch weight 产生GPU通信,如果想要 domino ep 正常工作,是不是应该将它放到 dispatcher.dispatch 函数中?

Comment thread xtuner/v1/module/dispatcher/moonep.py Outdated
raise NotImplementedError("MoonEP fixed-S training dispatch does not implement decoding")
grad_slot = self._next_gradient_slot
self._next_gradient_slot = (grad_slot + 1) % self._runtime._intra_layer_micro_batch
return _MoonEPInvocation(self._runtime, layer_id=self._layer_id, grad_slot=grad_slot).dispatch(

@jayhenry jayhenry Sep 2, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

现在 dispatcher太浅,只做简单的转发。将 Invocation 退化成一个对象类,并没有 dispatch, combine 等方法,让 dispatcher 本身有具体的dispatch等逻辑,这样改是否合理?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant