3131def qwen35_vl_35b_a3b_pretrain_8gpu_vrnvl72_bf16_config () -> ConfigContainer :
3232 """Qwen3.5-VL 35B-A3B pretrain: 8× VRNVL72, BF16 (alias of GB300)."""
3333 cfg = _build_qwen35_vl_35b_a3b_gb300_bf16 ()
34- # The GB300 base selects the cuDNN GDN kernel, which has not been measured on VRNVL72; keep FLA until it is.
34+ # The GB300 base selects the cuDNN GDN kernel. On VRNVL72 it was measured only with MXFP8 (against FLA
35+ # at 35B), so this precision keeps FLA until it is measured too.
3536 _select_gdn_kernel_backend (cfg , "fla" )
3637 # Keep process settings next to the recipe so users can see the exact benchmark environment.
3738 cfg .env_vars = {
@@ -59,7 +60,8 @@ def qwen35_vl_35b_a3b_pretrain_8gpu_vrnvl72_bf16_config() -> ConfigContainer:
5960def qwen35_vl_35b_a3b_pretrain_8gpu_vrnvl72_fp8cs_config () -> ConfigContainer :
6061 """Qwen3.5-VL 35B-A3B pretrain: 8× VRNVL72, FP8-CS (alias of GB300)."""
6162 cfg = qwen35_vl_35b_a3b_pretrain_8gpu_gb300_fp8cs_config ()
62- # The GB300 base selects the cuDNN GDN kernel, which has not been measured on VRNVL72; keep FLA until it is.
63+ # The GB300 base selects the cuDNN GDN kernel. On VRNVL72 it was measured only with MXFP8 (against FLA
64+ # at 35B), so this precision keeps FLA until it is measured too.
6365 _select_gdn_kernel_backend (cfg , "fla" )
6466 # Keep process settings next to the recipe so users can see the exact benchmark environment.
6567 cfg .env_vars = {
@@ -85,29 +87,79 @@ def qwen35_vl_35b_a3b_pretrain_8gpu_vrnvl72_fp8cs_config() -> ConfigContainer:
8587
8688
8789def qwen35_vl_35b_a3b_pretrain_8gpu_vrnvl72_fp8mx_config () -> ConfigContainer :
88- """Qwen3.5-VL 35B-A3B pretrain: 8× VRNVL72, FP8-MX (alias of GB300)."""
90+ """Qwen3.5-VL 35B-A3B pretrain: 8× VRNVL72, MXFP8, EP=4, HybridEP.
91+
92+ Starts from the GB300 MXFP8 recipe (CuTe DSL fused grouped MLP, cuDNN GDN, per-layer CUDA graphs, forced load
93+ balancing) and applies EP 4, MBS 4 and the kernel settings of the measured VRNVL72 runs, at GBS 1024.
94+ """
8995 cfg = qwen35_vl_35b_a3b_pretrain_8gpu_gb300_fp8mx_config ()
90- # The GB300 base selects the cuDNN GDN kernel, which has not been measured on VRNVL72; keep FLA until it is.
91- _select_gdn_kernel_backend (cfg , "fla" )
96+
97+ # EP 4 and MBS 4 as in the measured VRNVL72 runs: each expert-parallel group is one 4-GPU node
98+ # (NUM_OF_HYBRID_EP_RANKS_PER_NVLINK_DOMAIN below must equal EP). GBS 1024 gives 32 microbatches
99+ # per step on 8 GPUs.
100+ cfg .model .expert_model_parallel_size = 4
101+ cfg .train .micro_batch_size = 4
102+ cfg .train .global_batch_size = 1024
103+
104+ # The mock VLM loader runs the HF image processor on the CPU. One worker per rank supplies fewer
105+ # samples than this batch size consumes on VRNVL72, which would make the step wait for data.
106+ cfg .dataset .num_workers = 8
107+
108+ # Measured on VRNVL72 with MXFP8: keep the cuDNN GDN kernel that the GB300 base selects.
109+ _select_gdn_kernel_backend (cfg , "transformer_engine" )
110+
111+ # Run the shared expert through Transformer Engine's grouped GEMM with the same 32-wide GLU
112+ # interleaving as the routed experts.
113+ cfg .model .use_grouped_gemm_for_shared_expert = True
114+ cfg .model .moe_shared_expert_glu_interleave_size = 32
115+
116+ # HybridEP: permute outside the dispatcher (already the default; stated explicitly because fusing
117+ # the permute into HybridEP measured slower on VRNVL72), 16 SMs for preprocessing as in the measured
118+ # runs, and overlap the dispatch backward with the expert weight-gradient GEMMs. Megatron-Core does
119+ # not allow this overlap together with EP A2A overlap or delay_wgrad_compute.
120+ cfg .model .moe_permute_fusion_into_hybridep = False
121+ cfg .model .moe_hybridep_num_sms_preprocessing = 16
122+ cfg .model .overlap_dispatch_backward_with_experts_wgrad = True
123+
124+ # Vision-encoder CUDA graph. The cap is the number of vision patches per microbatch: a longer
125+ # vision sequence stops the run, and a shorter one is padded to the cap on every replay, so set it
126+ # to MBS x images per sample x patches per image whenever one of them changes. The Qwen-VL data
127+ # path resizes each 256x256 mock image twice: qwen_vl_utils snaps it to 252x252, then the HF
128+ # processor scales it up to at least QWEN_VL_MIN_PIXELS (448 * 448) pixels in area. Each image
129+ # therefore reaches the encoder as 448x448 = 28x28 patches of 16x16 pixels, 784 patches, and MBS 4
130+ # gives 3136. With the graph, training runs the encoder as one dense attention over all images of a
131+ # microbatch (images attend to each other, and padding is not masked) and without the 2D rotary
132+ # embedding, which the capture does not record; evaluation runs eagerly, per image and with the
133+ # rotary embedding. vision_cuda_graph_impl = "none" restores that behavior in training as well.
134+ cfg .model .vision_cuda_graph_impl = "transformer_engine"
135+ cfg .model .vision_cuda_graph_scope = ["attn" , "mlp" ]
136+ cfg .model .max_vision_cuda_graph_seq_length = 3136
137+
92138 # Keep process settings next to the recipe so users can see the exact benchmark environment.
93139 cfg .env_vars = {
94140 ** COMMON_PERF_ENV_VARS ,
95141 # CUDA stream scheduling for this model and parallel layout.
96142 "CUDA_DEVICE_MAX_CONNECTIONS" : 32 ,
97143 # CUDA graph and allocator behavior for this recipe.
98144 "NCCL_GRAPH_REGISTER" : 0 ,
99- "PYTORCH_CUDA_ALLOC_CONF" : "expandable_segments:True" ,
100- "TORCH_NCCL_AVOID_RECORD_STREAMS" : 1 ,
145+ "PYTORCH_CUDA_ALLOC_CONF" : "expandable_segments:True,graph_capture_record_stream_reuse:True " ,
146+ "TORCH_NCCL_AVOID_RECORD_STREAMS" : 0 ,
101147 # NCCL user-buffer and launch settings.
102148 "NCCL_NVLS_ENABLE" : 0 ,
103- # HybridEP topology for the target system.
104- "NUM_OF_HYBRID_EP_RANKS_PER_NVLINK_DOMAIN" : 8 ,
149+ # HybridEP topology for the target system: one rank per GPU of the EP 4 group .
150+ "NUM_OF_HYBRID_EP_RANKS_PER_NVLINK_DOMAIN" : 4 ,
105151 "NUM_OF_TOKENS_PER_CHUNK_COMBINE_API" : 128 ,
106152 "NVLINK_DOMAIN_SIZE" : 72 ,
107153 "USE_MNNVL" : 1 ,
108154 # Transformer Engine overlap settings for this model.
109- "NVTE_BWD_LAYERNORM_SM_MARGIN" : 20 ,
110- "NVTE_FWD_LAYERNORM_SM_MARGIN" : 20 ,
155+ "NVTE_BWD_LAYERNORM_SM_MARGIN" : 0 ,
156+ "NVTE_FWD_LAYERNORM_SM_MARGIN" : 0 ,
157+ # cuDNN normalization kernels.
158+ "NVTE_NORM_BWD_USE_CUDNN" : 1 ,
159+ "NVTE_NORM_FWD_USE_CUDNN" : 1 ,
160+ # Gate for the CuTe DSL fused grouped MLP that the GB300 base enables. Transformer Engine
161+ # checks it when it registers the fusion; without it the op fuser runs the unfused path.
162+ "NVTE_CUTEDSL_FUSED_GROUPED_MLP" : 1 ,
111163 }
112164 return cfg
113165
@@ -193,7 +245,8 @@ def qwen35_vl_122b_a10b_pretrain_32gpu_vrnvl72_fp8mx_config() -> ConfigContainer
193245def qwen35_vl_397b_a17b_pretrain_64gpu_vrnvl72_bf16_config () -> ConfigContainer :
194246 """Qwen3.5-VL 397B-A17B pretrain: 64× VRNVL72, BF16 (alias of GB300)."""
195247 cfg = qwen35_vl_397b_a17b_pretrain_64gpu_gb300_bf16_config ()
196- # The GB300 base selects the cuDNN GDN kernel, which has not been measured on VRNVL72; keep FLA until it is.
248+ # The GB300 base selects the cuDNN GDN kernel. On VRNVL72 it was measured only with MXFP8 (against FLA
249+ # at 35B), so this precision keeps FLA until it is measured too.
197250 _select_gdn_kernel_backend (cfg , "fla" )
198251 # Keep process settings next to the recipe so users can see the exact benchmark environment.
199252 cfg .env_vars = {
@@ -221,7 +274,8 @@ def qwen35_vl_397b_a17b_pretrain_64gpu_vrnvl72_bf16_config() -> ConfigContainer:
221274def qwen35_vl_397b_a17b_pretrain_64gpu_vrnvl72_fp8cs_config () -> ConfigContainer :
222275 """Qwen3.5-VL 397B-A17B pretrain: 64× VRNVL72, FP8-CS (alias of GB300)."""
223276 cfg = qwen35_vl_397b_a17b_pretrain_64gpu_gb300_fp8cs_config ()
224- # The GB300 base selects the cuDNN GDN kernel, which has not been measured on VRNVL72; keep FLA until it is.
277+ # The GB300 base selects the cuDNN GDN kernel. On VRNVL72 it was measured only with MXFP8 (against FLA
278+ # at 35B), so this precision keeps FLA until it is measured too.
225279 _select_gdn_kernel_backend (cfg , "fla" )
226280 # Keep process settings next to the recipe so users can see the exact benchmark environment.
227281 cfg .env_vars = {
@@ -247,10 +301,46 @@ def qwen35_vl_397b_a17b_pretrain_64gpu_vrnvl72_fp8cs_config() -> ConfigContainer
247301
248302
249303def qwen35_vl_397b_a17b_pretrain_64gpu_vrnvl72_fp8mx_config () -> ConfigContainer :
250- """Qwen3.5-VL 397B-A17B pretrain: 64× VRNVL72, FP8-MX (alias of GB300)."""
304+ """Qwen3.5-VL 397B-A17B pretrain: 64× VRNVL72, MXFP8, EP=32, HybridEP.
305+
306+ Starts from the GB300 MXFP8 recipe (cuDNN GDN, per-layer CUDA graphs, forced load balancing) and adds the
307+ CuTe DSL fused grouped MLP and the settings of the measured VRNVL72 run.
308+ """
251309 cfg = qwen35_vl_397b_a17b_pretrain_64gpu_gb300_fp8mx_config ()
252- # The GB300 base selects the cuDNN GDN kernel, which has not been measured on VRNVL72; keep FLA until it is.
253- _select_gdn_kernel_backend (cfg , "fla" )
310+
311+ # MBS 1, as in the GB300 base; the vision cap below is set for it.
312+ cfg .train .micro_batch_size = 1
313+
314+ # Keep the cuDNN GDN kernel that the GB300 base selects: it measured faster than FLA on VRNVL72 at
315+ # 35B, and the measured 397B run used it.
316+ _select_gdn_kernel_backend (cfg , "transformer_engine" )
317+
318+ # Route the experts through Transformer Engine's CuTe DSL fused grouped MLP (FC1 + SwiGLU + FC2 for
319+ # MXFP8). The GB300 397B base does not enable the op fuser (it is off by default), so it is enabled
320+ # here; with it, Megatron-Core also runs the experts on grouped tensors. The op fuser, the 32-wide
321+ # GLU interleaving and NVTE_CUTEDSL_FUSED_GROUPED_MLP below are required together.
322+ cfg .model .use_transformer_engine_op_fuser = True
323+ cfg .model .moe_mlp_glu_interleave_size = 32
324+
325+ # Recompute the GDN output norm, as the measured run did in effect. That run also listed core_attn, but
326+ # all of its attention layers ran inside CUDA graphs, where Megatron-Core does not recompute. The vision
327+ # encoder inherits selective recompute of its attention (vision_recompute_granularity="inherit"); inside
328+ # the vision CUDA graph below that has no effect either, but it applies once the vision graph is off.
329+ cfg .model .recompute_granularity = "selective"
330+ cfg .model .recompute_modules = ["gdn_norm_out" ]
331+
332+ # HybridEP: permute outside the dispatcher. This is already the default; it is stated explicitly
333+ # because fusing the permute into HybridEP measured slower on VRNVL72.
334+ cfg .model .moe_permute_fusion_into_hybridep = False
335+
336+ # Vision-encoder CUDA graph with the cap for MBS 1: each 256x256 mock image reaches the encoder as
337+ # 784 patches (see the 35B recipe above). Set the cap to MBS x images per sample x patches per image
338+ # whenever one of them changes. As in the 35B recipe, the graphed encoder runs without the 2D rotary
339+ # embedding, and images that share a microbatch attend to each other.
340+ cfg .model .vision_cuda_graph_impl = "transformer_engine"
341+ cfg .model .vision_cuda_graph_scope = ["attn" , "mlp" ]
342+ cfg .model .max_vision_cuda_graph_seq_length = 784
343+
254344 # Keep process settings next to the recipe so users can see the exact benchmark environment.
255345 cfg .env_vars = {
256346 ** COMMON_PERF_ENV_VARS ,
@@ -267,8 +357,14 @@ def qwen35_vl_397b_a17b_pretrain_64gpu_vrnvl72_fp8mx_config() -> ConfigContainer
267357 "NUM_OF_TOKENS_PER_CHUNK_COMBINE_API" : 128 ,
268358 "NVLINK_DOMAIN_SIZE" : 72 ,
269359 "USE_MNNVL" : 1 ,
360+ # HybridEP dispatch and preprocessing chunks of the measured run (HybridEP's default is 64).
361+ "NUM_OF_TOKENS_PER_CHUNK_DISPATCH_API" : 128 ,
362+ "NUM_OF_TOKENS_PER_CHUNK_PREPROCESSING_API" : 128 ,
270363 # Transformer Engine overlap settings for this model.
271- "NVTE_BWD_LAYERNORM_SM_MARGIN" : 20 ,
272- "NVTE_FWD_LAYERNORM_SM_MARGIN" : 20 ,
364+ "NVTE_BWD_LAYERNORM_SM_MARGIN" : 16 ,
365+ "NVTE_FWD_LAYERNORM_SM_MARGIN" : 16 ,
366+ # Gate for the CuTe DSL fused grouped MLP enabled above. Transformer Engine checks it when it
367+ # registers the fusion; without it the op fuser runs the unfused path.
368+ "NVTE_CUTEDSL_FUSED_GROUPED_MLP" : 1 ,
273369 }
274370 return cfg
0 commit comments