Skip to content

Commit a46fa53

Browse files
committed
test(functional): cover additional moe perf recipes
Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
1 parent 6c63214 commit a46fa53

4 files changed

Lines changed: 214 additions & 113 deletions

File tree

tests/functional_tests/launch_scripts/gb200/active/L0_Launch_recipes_qwen3_moe_perf_proxy.sh renamed to tests/functional_tests/launch_scripts/gb200/active/L0_Launch_recipes_moe_perf_proxies.sh

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
#!/bin/bash
22
# GPU_COUNT=x8
3-
# CI_TIMEOUT=30
3+
# CI_TIMEOUT=60
44
# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved.
55
#
66
# Licensed under the Apache License, Version 2.0 (the "License");
77
# you may not use this file except in compliance with the License.
88
# You may obtain a copy of the License at
99
#
10-
# http://www.apache.org/licenses/LICENSE-2.0
10+
# http://www.apache.org/licenses/LICENSE-2.0
1111
#
1212
# Unless required by applicable law or agreed to in writing, software
1313
# distributed under the License is distributed on an "AS IS" BASIS,
@@ -27,15 +27,30 @@ export NCCL_NVLS_ENABLE=0
2727
export NUM_OF_HYBRID_EP_RANKS_PER_NVLINK_DOMAIN=8
2828
export NUM_OF_TOKENS_PER_CHUNK_COMBINE_API=128
2929
export NVLINK_DOMAIN_SIZE=72
30+
export NVTE_BWD_LAYERNORM_SM_MARGIN=20
3031
export NVTE_CUTEDSL_FUSED_GROUPED_MLP=1
32+
export NVTE_FWD_LAYERNORM_SM_MARGIN=20
3133
export PYTORCH_CUDA_ALLOC_CONF="expandable_segments:True"
34+
export TOKENIZERS_PARALLELISM=False
3235
export TORCH_NCCL_AVOID_RECORD_STREAMS=1
3336
export TORCH_NCCL_HIGH_PRIORITY=1
3437
export USE_MNNVL=1
3538

36-
uv run python -m torch.distributed.run --nproc_per_node=8 --nnodes=1 -m coverage run \
37-
--data-file=/opt/Megatron-Bridge/.coverage --source=/opt/Megatron-Bridge/ --parallel-mode \
38-
-m pytest -o log_cli=true -o log_cli_level=INFO -v -s -x -m "not pleasefixme" --tb=short -rA \
39-
tests/functional_tests/test_groups/recipes/test_qwen3_moe_perf_proxy.py::TestQwen3MoePerfProxy::test_gb200_fp8mx
39+
run_proxy() {
40+
uv run python -m torch.distributed.run --nproc_per_node=8 --nnodes=1 -m coverage run \
41+
--data-file=/opt/Megatron-Bridge/.coverage --source=/opt/Megatron-Bridge/ --parallel-mode \
42+
-m pytest -o log_cli=true -o log_cli_level=INFO -v -s -x -m "not pleasefixme" --tb=short -rA "$1"
43+
}
44+
45+
run_proxy \
46+
tests/functional_tests/test_groups/recipes/test_moe_perf_proxies.py::TestQwen3MoePerfProxy::test_gb200_fp8mx
47+
run_proxy \
48+
tests/functional_tests/test_groups/recipes/test_moe_perf_proxies.py::TestAdditionalMoePerfProxies::test_gpt_oss_120b_gb200_fp8mx
49+
50+
export NVTE_ALLOW_NONDETERMINISTIC_ALGO=0
51+
export NVTE_NORM_BWD_USE_CUDNN=1
52+
export NVTE_NORM_FWD_USE_CUDNN=1
53+
run_proxy \
54+
tests/functional_tests/test_groups/recipes/test_moe_perf_proxies.py::TestAdditionalMoePerfProxies::test_deepseek_v3_gb200_fp8mx
4055

4156
coverage combine -q

tests/functional_tests/launch_scripts/h100/active/L0_Launch_recipes_qwen3_moe_perf_proxy.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,6 @@ export USE_MNNVL=0
3232
uv run python -m torch.distributed.run --nproc_per_node=8 --nnodes=1 -m coverage run \
3333
--data-file=/opt/Megatron-Bridge/.coverage --source=/opt/Megatron-Bridge/ --parallel-mode \
3434
-m pytest -o log_cli=true -o log_cli_level=INFO -v -s -x -m "not pleasefixme" --tb=short -rA \
35-
tests/functional_tests/test_groups/recipes/test_qwen3_moe_perf_proxy.py::TestQwen3MoePerfProxy::test_h100_fp8cs
35+
tests/functional_tests/test_groups/recipes/test_moe_perf_proxies.py::TestQwen3MoePerfProxy::test_h100_fp8cs
3636

3737
coverage combine -q
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Functional proxies for production MoE performance recipes.
16+
17+
These proxies cover the model families that failed together in nemo-ci issue
18+
#4094. They start from the exact production performance constructors and only
19+
reduce topology or model depth enough to fit one eight-GPU runner. Assertions
20+
pin the MXFP8, natural-routing, HybridEP, TE op-fuser, and CUDA-graph behavior
21+
that exposed the historical grouped-MLP failure.
22+
"""
23+
24+
import os
25+
from collections.abc import Callable
26+
27+
import pytest
28+
import torch
29+
30+
from megatron.bridge.perf_recipes.deepseek import deepseek_v3_pretrain_256gpu_gb200_fp8mx_config
31+
from megatron.bridge.perf_recipes.gpt_oss import gpt_oss_120b_pretrain_64gpu_gb200_fp8mx_config
32+
from megatron.bridge.perf_recipes.qwen import (
33+
qwen3_30b_a3b_pretrain_8gpu_gb200_fp8mx_config,
34+
qwen3_30b_a3b_pretrain_16gpu_h100_fp8cs_config,
35+
)
36+
from megatron.bridge.training.config import ConfigContainer
37+
from tests.functional_tests.test_groups.recipes.utils import run_pretrain_recipe_perf_test
38+
39+
40+
def _use_null_tokenizer(config: ConfigContainer) -> None:
41+
"""Keep mock-data proxies independent of external tokenizer downloads."""
42+
config.tokenizer.tokenizer_type = "NullTokenizer"
43+
config.tokenizer.tokenizer_model = None
44+
config.tokenizer.vocab_size = config.model.vocab_size
45+
46+
47+
def _assert_hybridep_natural_routing(config: ConfigContainer) -> None:
48+
"""Guard the shared dispatch path implicated in the historical failures."""
49+
assert config.model.moe_grouped_gemm is True
50+
assert config.model.moe_token_dispatcher_type == "flex"
51+
assert config.model.moe_flex_dispatcher_backend == "hybridep"
52+
assert config.model.moe_router_force_load_balancing is False
53+
54+
55+
def _assert_blackwell_mxfp8_path(config: ConfigContainer) -> None:
56+
"""Guard the exact Blackwell TE grouped-MLP path fixed by the paired PR."""
57+
assert os.environ.get("NVTE_CUTEDSL_FUSED_GROUPED_MLP") == "1"
58+
assert config.mixed_precision.fp8_recipe == "mxfp8"
59+
assert config.model.use_transformer_engine_op_fuser is True
60+
assert config.model.moe_single_grouped_weight is False
61+
assert config.model.moe_mlp_glu_interleave_size == 32
62+
assert config.model.high_priority_a2a_comm_stream is True
63+
assert config.comm_overlap.overlap_moe_expert_parallel_comm is True
64+
assert config.comm_overlap.delay_wgrad_compute is True
65+
66+
67+
def _qwen3_moe_proxy(
68+
config_func: Callable[[], ConfigContainer],
69+
*,
70+
expert_model_parallel_size: int,
71+
) -> ConfigContainer:
72+
config = config_func()
73+
config.model.num_layers = 2
74+
config.model.expert_model_parallel_size = expert_model_parallel_size
75+
_use_null_tokenizer(config)
76+
77+
assert config.model.num_moe_experts == 128
78+
assert config.model.moe_router_topk == 8
79+
_assert_hybridep_natural_routing(config)
80+
return config
81+
82+
83+
def _deepseek_v3_gb200_proxy() -> ConfigContainer:
84+
"""Shrink DeepSeek V3 while preserving PP, MTP, and the production MoE path."""
85+
config = deepseek_v3_pretrain_256gpu_gb200_fp8mx_config()
86+
87+
# Four decoder layers allow every PP rank to own useful work while keeping
88+
# MTP and loss colocated on the final stage, as in the production layout.
89+
config.model.num_layers = 4
90+
config.model.moe_layer_freq = [0, 1, 1, 1]
91+
config.model.num_moe_experts = 16
92+
config.model.tensor_model_parallel_size = 1
93+
config.model.pipeline_model_parallel_size = 4
94+
config.model.virtual_pipeline_model_parallel_size = None
95+
config.model.context_parallel_size = 1
96+
config.model.expert_model_parallel_size = 2
97+
config.model.pipeline_model_parallel_layout = [
98+
["embedding", "decoder"],
99+
["decoder"],
100+
["decoder"],
101+
["decoder", "mtp", "loss"],
102+
]
103+
_use_null_tokenizer(config)
104+
105+
assert config.model.mtp_num_layers == 1
106+
assert config.model.moe_router_topk == 8
107+
assert config.model.cuda_graph_impl == "full_iteration"
108+
assert config.model.cuda_graph_scope == []
109+
assert config.model.moe_paged_stash is True
110+
assert config.model.moe_pad_experts_for_cuda_graph_inference is True
111+
assert config.model.moe_expert_rank_capacity_factor == 1.5
112+
assert config.model.fp8_output_proj is True
113+
_assert_hybridep_natural_routing(config)
114+
_assert_blackwell_mxfp8_path(config)
115+
return config
116+
117+
118+
def _gpt_oss_120b_gb200_proxy() -> ConfigContainer:
119+
"""Shrink GPT-OSS depth while preserving its 120B provider and EP path."""
120+
config = gpt_oss_120b_pretrain_64gpu_gb200_fp8mx_config()
121+
config.model.num_layers = 2
122+
config.model.tensor_model_parallel_size = 1
123+
config.model.pipeline_model_parallel_size = 1
124+
config.model.virtual_pipeline_model_parallel_size = None
125+
config.model.context_parallel_size = 1
126+
config.model.expert_model_parallel_size = 8
127+
_use_null_tokenizer(config)
128+
129+
assert config.model.num_moe_experts == 128
130+
assert config.model.moe_router_topk == 4
131+
assert config.model.window_attn_skip_freq == 2
132+
assert config.model.cuda_graph_impl == "full_iteration"
133+
assert config.model.cuda_graph_scope == []
134+
assert config.model.moe_paged_stash is True
135+
assert config.model.moe_pad_experts_for_cuda_graph_inference is True
136+
assert config.model.moe_expert_rank_capacity_factor == 1.5
137+
_assert_hybridep_natural_routing(config)
138+
_assert_blackwell_mxfp8_path(config)
139+
return config
140+
141+
142+
class TestQwen3MoePerfProxy:
143+
"""Train reduced Qwen3 production configs on matching GPU runners."""
144+
145+
@pytest.mark.run_only_on("GPU")
146+
def test_h100_fp8cs(self):
147+
assert torch.cuda.get_device_capability()[0] == 9, "The H100 proxy requires Hopper GPUs."
148+
149+
def proxy_config() -> ConfigContainer:
150+
config = _qwen3_moe_proxy(
151+
qwen3_30b_a3b_pretrain_16gpu_h100_fp8cs_config,
152+
expert_model_parallel_size=8,
153+
)
154+
assert config.mixed_precision.fp8 is not None
155+
assert config.mixed_precision.fp8_recipe == "tensorwise"
156+
return config
157+
158+
run_pretrain_recipe_perf_test(proxy_config, "qwen3_30b_a3b_h100_fp8cs_proxy")
159+
160+
@pytest.mark.run_only_on("GPU")
161+
def test_gb200_fp8mx(self):
162+
assert torch.cuda.get_device_capability()[0] >= 10, "The GB200 MXFP8 proxy requires Blackwell GPUs."
163+
164+
def proxy_config() -> ConfigContainer:
165+
config = _qwen3_moe_proxy(
166+
qwen3_30b_a3b_pretrain_8gpu_gb200_fp8mx_config,
167+
expert_model_parallel_size=8,
168+
)
169+
assert config.mixed_precision.fp8_dot_product_attention is True
170+
assert config.model.cuda_graph_impl == "transformer_engine"
171+
assert config.model.cuda_graph_scope == ["attn", "moe_router", "moe_preprocess"]
172+
assert config.model.moe_paged_stash is False
173+
assert config.model.moe_pad_experts_for_cuda_graph_inference is False
174+
assert config.model.moe_expert_rank_capacity_factor is None
175+
_assert_blackwell_mxfp8_path(config)
176+
return config
177+
178+
run_pretrain_recipe_perf_test(proxy_config, "qwen3_30b_a3b_gb200_fp8mx_proxy")
179+
180+
181+
class TestAdditionalMoePerfProxies:
182+
"""Cover the DeepSeek V3 and GPT-OSS 120B failures missed by Qwen alone."""
183+
184+
@pytest.mark.run_only_on("GPU")
185+
def test_deepseek_v3_gb200_fp8mx(self):
186+
assert torch.cuda.get_device_capability()[0] >= 10, "The GB200 MXFP8 proxy requires Blackwell GPUs."
187+
run_pretrain_recipe_perf_test(_deepseek_v3_gb200_proxy, "deepseek_v3_gb200_fp8mx_proxy")
188+
189+
@pytest.mark.run_only_on("GPU")
190+
def test_gpt_oss_120b_gb200_fp8mx(self):
191+
assert torch.cuda.get_device_capability()[0] >= 10, "The GB200 MXFP8 proxy requires Blackwell GPUs."
192+
run_pretrain_recipe_perf_test(_gpt_oss_120b_gb200_proxy, "gpt_oss_120b_gb200_fp8mx_proxy")

tests/functional_tests/test_groups/recipes/test_qwen3_moe_perf_proxy.py

Lines changed: 0 additions & 106 deletions
This file was deleted.

0 commit comments

Comments
 (0)