|
115 | 115 | ] |
116 | 116 |
|
117 | 117 | _QWEN35_VL_GB300_FUNCS = [ |
| 118 | + _qwen35_vl_gb300_module.qwen35_vl_35b_a3b_pretrain_8gpu_gb300_fp8mx_library_config, |
118 | 119 | _qwen35_vl_gb300_module.qwen35_vl_35b_a3b_pretrain_16gpu_gb300_bf16_config, |
119 | 120 | _qwen35_vl_gb300_module.qwen35_vl_397b_a17b_pretrain_config, |
120 | 121 | ] |
@@ -514,6 +515,120 @@ def test_qwen35_vl_35b_a3b_pretrain_16gpu_gb300_defaults(monkeypatch: pytest.Mon |
514 | 515 | assert cfg.checkpoint.pretrained_checkpoint is None |
515 | 516 |
|
516 | 517 |
|
| 518 | +def test_qwen35_vl_35b_a3b_pretrain_8gpu_gb300_fp8mx_library_defaults(monkeypatch: pytest.MonkeyPatch): |
| 519 | + """The 8-GB300 MXFP8 recipe adds the measured precision, kernel and dispatcher settings to the 16-GPU recipe.""" |
| 520 | + patch_recipe_module_global(monkeypatch, _qwen35_vl_h100_module, "AutoBridge", _FakeAutoBridge) |
| 521 | + |
| 522 | + cfg = _qwen35_vl_gb300_module.qwen35_vl_35b_a3b_pretrain_8gpu_gb300_fp8mx_library_config() |
| 523 | + base = _qwen35_vl_gb300_module.qwen35_vl_35b_a3b_pretrain_16gpu_gb300_bf16_config() |
| 524 | + |
| 525 | + _assert_basic_config(cfg) |
| 526 | + # Same parallel layout, CUDA graphs, recompute, batch shape and routing as the 16-GPU recipe. |
| 527 | + assert cfg.model.expert_model_parallel_size == 4 |
| 528 | + assert cfg.env_vars["NUM_OF_HYBRID_EP_RANKS_PER_NVLINK_DOMAIN"] == 4 |
| 529 | + assert cfg.train.global_batch_size == base.train.global_batch_size == 2048 |
| 530 | + assert cfg.train.micro_batch_size == base.train.micro_batch_size == 4 |
| 531 | + assert cfg.model.max_vision_cuda_graph_seq_length == 3712 |
| 532 | + assert cuda_graph_module_names(cfg.model) == ["attn", "moe_router", "moe_preprocess"] |
| 533 | + assert cfg.model.recompute_modules == ["core_attn", "gdn_norm_out"] |
| 534 | + assert cfg.model.moe_router_force_load_balancing is False |
| 535 | + # MXFP8 with FP8 parameter all-gather. |
| 536 | + assert cfg.mixed_precision.fp8 == "e4m3" |
| 537 | + assert cfg.mixed_precision.fp8_recipe == "mxfp8" |
| 538 | + assert cfg.mixed_precision.fp8_param_gather is True |
| 539 | + assert cfg.mixed_precision.reuse_grad_buf_for_mxfp8_param_ag is True |
| 540 | + assert cfg.mixed_precision.grad_reduce_in_fp32 is False |
| 541 | + # CuTe DSL fused grouped MLP needs all three settings together. |
| 542 | + assert cfg.model.use_transformer_engine_op_fuser is True |
| 543 | + assert cfg.model.moe_mlp_glu_interleave_size == 32 |
| 544 | + assert cfg.env_vars["NVTE_CUTEDSL_FUSED_GROUPED_MLP"] == 1 |
| 545 | + assert cfg.model.moe_flex_dispatcher_num_sms == 32 |
| 546 | + assert cfg.model.moe_permute_fusion_into_hybridep is False |
| 547 | + assert cfg.model.moe_hybridep_num_sms_preprocessing == 16 |
| 548 | + assert cfg.model.overlap_dispatch_backward_with_experts_wgrad is True |
| 549 | + assert cfg.env_vars["NUM_OF_TOKENS_PER_CHUNK_COMBINE_API"] == 128 |
| 550 | + assert cfg.env_vars["NUM_OF_TOKENS_PER_CHUNK_DISPATCH_API"] == 64 |
| 551 | + assert cfg.env_vars["NUM_OF_TOKENS_PER_CHUNK_PREPROCESSING_API"] == 64 |
| 552 | + assert cfg.env_vars["NVTE_NORM_FWD_USE_CUDNN"] == 1 |
| 553 | + assert cfg.env_vars["NVTE_NORM_BWD_USE_CUDNN"] == 1 |
| 554 | + # Building the 8-GPU recipe must not leak the env gate into the 16-GPU recipe. |
| 555 | + assert "NVTE_CUTEDSL_FUSED_GROUPED_MLP" not in base.env_vars |
| 556 | + |
| 557 | + # These are the only model-field and environment-variable differences from the 16-GPU recipe; an extra model |
| 558 | + # field or environment variable fails here. Mixed precision is checked above on five fields only, and other |
| 559 | + # sections, such as train, optimizer, ddp and comm_overlap, are not compared. |
| 560 | + changed_model_fields = { |
| 561 | + name |
| 562 | + for name in vars(cfg.model).keys() | vars(base.model).keys() |
| 563 | + if getattr(cfg.model, name, None) != getattr(base.model, name, None) |
| 564 | + } |
| 565 | + assert changed_model_fields == { |
| 566 | + "use_transformer_engine_op_fuser", |
| 567 | + "moe_mlp_glu_interleave_size", |
| 568 | + "moe_flex_dispatcher_num_sms", |
| 569 | + "moe_permute_fusion_into_hybridep", |
| 570 | + "moe_hybridep_num_sms_preprocessing", |
| 571 | + "overlap_dispatch_backward_with_experts_wgrad", |
| 572 | + } |
| 573 | + changed_env_vars = { |
| 574 | + name |
| 575 | + for name in cfg.env_vars.keys() | base.env_vars.keys() |
| 576 | + if cfg.env_vars.get(name) != base.env_vars.get(name) |
| 577 | + } |
| 578 | + assert changed_env_vars == { |
| 579 | + "NVTE_CUTEDSL_FUSED_GROUPED_MLP", |
| 580 | + "NUM_OF_TOKENS_PER_CHUNK_DISPATCH_API", |
| 581 | + "NUM_OF_TOKENS_PER_CHUNK_PREPROCESSING_API", |
| 582 | + "NVTE_NORM_FWD_USE_CUDNN", |
| 583 | + "NVTE_NORM_BWD_USE_CUDNN", |
| 584 | + } |
| 585 | + |
| 586 | + |
| 587 | +class _FakeModelCfgWithGdnKernelBackend(_FakeModelCfg): |
| 588 | + """Model config from a Megatron-Core that declares gdn_kernel_backend (NVIDIA/Megatron-LM#7583).""" |
| 589 | + |
| 590 | + # Megatron-Core's default; a class attribute, as on the real dataclass. |
| 591 | + gdn_kernel_backend = "fla" |
| 592 | + |
| 593 | + |
| 594 | +class _FakeAutoBridgeWithGdnKernelBackend(_FakeAutoBridge): |
| 595 | + """AutoBridge yielding a provider that declares gdn_kernel_backend.""" |
| 596 | + |
| 597 | + @staticmethod |
| 598 | + def from_hf_pretrained(hf_path: str, *, revision: str | None = None): |
| 599 | + return _FakeAutoBridgeWithGdnKernelBackend() |
| 600 | + |
| 601 | + def to_megatron_provider(self, load_weights: bool = False): |
| 602 | + return _FakeModelCfgWithGdnKernelBackend() |
| 603 | + |
| 604 | + |
| 605 | +def test_qwen35_vl_35b_a3b_pretrain_8gpu_gb300_fp8mx_library_selects_cudnn_gdn(monkeypatch: pytest.MonkeyPatch): |
| 606 | + """If Megatron-Core has gdn_kernel_backend, the 8-GPU recipe selects cuDNN GDN; the 16-GPU one keeps FLA.""" |
| 607 | + patch_recipe_module_global(monkeypatch, _qwen35_vl_h100_module, "AutoBridge", _FakeAutoBridgeWithGdnKernelBackend) |
| 608 | + |
| 609 | + cfg = _qwen35_vl_gb300_module.qwen35_vl_35b_a3b_pretrain_8gpu_gb300_fp8mx_library_config() |
| 610 | + base = _qwen35_vl_gb300_module.qwen35_vl_35b_a3b_pretrain_16gpu_gb300_bf16_config() |
| 611 | + |
| 612 | + assert cfg.model.gdn_kernel_backend == "transformer_engine" |
| 613 | + # The 16-GPU BF16 recipe keeps Megatron-Core's default. |
| 614 | + assert base.model.gdn_kernel_backend == "fla" |
| 615 | + |
| 616 | + |
| 617 | +def test_qwen35_vl_35b_a3b_pretrain_8gpu_gb300_fp8mx_library_no_phantom_gdn_kernel_backend( |
| 618 | + monkeypatch: pytest.MonkeyPatch, |
| 619 | +): |
| 620 | + """On a Megatron-Core without gdn_kernel_backend the recipe must not invent the attribute. |
| 621 | +
|
| 622 | + Assigning an unknown field would not raise: it would create an unused attribute, so the recipe would look as if it |
| 623 | + selected cuDNN GDN while the layers keep running on FLA. |
| 624 | + """ |
| 625 | + patch_recipe_module_global(monkeypatch, _qwen35_vl_h100_module, "AutoBridge", _FakeAutoBridge) |
| 626 | + |
| 627 | + cfg = _qwen35_vl_gb300_module.qwen35_vl_35b_a3b_pretrain_8gpu_gb300_fp8mx_library_config() |
| 628 | + |
| 629 | + assert not hasattr(cfg.model, "gdn_kernel_backend") |
| 630 | + |
| 631 | + |
517 | 632 | def test_qwen35_vl_35b_a3b_pretrain_16gpu_h100_defaults(monkeypatch: pytest.MonkeyPatch): |
518 | 633 | """The 16-H100 library pretrain recipe should own the measured execution policy.""" |
519 | 634 | patch_recipe_module_global(monkeypatch, _qwen35_vl_h100_module, "AutoBridge", _FakeAutoBridge) |
|
0 commit comments