Repository navigation
Build TransformerConfig once in get_batch - #8525
Conversation
get_batch in pretrain_gpt.py and pretrain_hybrid.py rebuilt the transformer config from args on every microbatch, which re-runs TransformerConfig.__post_init__ and repeats its validation warnings in the log every step. Cache the config per args object so it is built once and rebuilt only when the global args object is replaced. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Deepak Narayanan <dnarayanan@nvidia.com>
|
Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually. Contributors can view more details about this message here. |
get_batch
get_batchTransformerConfig once in get_batch
TransformerConfig once in get_batchTransformerConfig once in get_batch
svcnvidia-nemo-ci
left a comment
There was a problem hiding this comment.
Add regression coverage for cache reuse and invalidation
The caches added in pretrain_gpt.py and pretrain_hybrid.py have no accompanying regression test. The existing mocked packing case and pretraining case fetch only one batch per args object and do not assert constructor counts. Restoring construction on every microbatch would leave those assertions unchanged, allowing the repeated-warning bug to return undetected. This is a coverage gap, not an observed runtime failure in the proposed implementation.
Add a parameterized regression to test_get_batch.py. Exercise get_batch so the test protects both the helper and its call-site wiring. Repeated calls with the same args must reuse the config, while replacement with an equal-valued but distinct args object must rebuild it.
Suggested addition using the file’s existing imports; source-reasoned and not executed, so verify before adopting:
@pytest.mark.parametrize("script", [pretrain_gpt, pretrain_hybrid], ids=["gpt", "hybrid"])
def test_get_batch_caches_config_until_args_replaced(script, monkeypatch):
args = SimpleNamespace(
sequence_packing_scheduler="dp_balanced", dynamic_context_parallel=False
)
configs = [
SimpleNamespace(
virtual_pipeline_model_parallel_size=None,
pipeline_model_parallel_layout=None,
mtp_num_layers=None,
)
for _ in range(2)
]
build_config = MagicMock(side_effect=configs)
result = object()
fetch_batch = MagicMock(return_value=result)
monkeypatch.setattr(script, "_transformer_config_cache", None)
monkeypatch.setattr(script, "get_args", lambda: args)
monkeypatch.setattr(script, "core_transformer_config_from_args", build_config)
monkeypatch.setattr(script, "mtp_on_this_rank_func", lambda **kwargs: False)
monkeypatch.setattr(script, "get_batch_on_this_rank_for_sequence_packing", fetch_batch)
pgs = ProcessGroupCollection(pp=object())
for _ in range(2):
assert script.get_batch(None, pg_collection=pgs) is result
assert fetch_batch.call_args.kwargs["config"] is configs[0]
build_config.assert_called_once_with(args)
previous_args = args
args = SimpleNamespace(**vars(previous_args))
assert args == previous_args and args is not previous_args
for _ in range(2):
assert script.get_batch(None, pg_collection=pgs) is result
assert fetch_batch.call_args.kwargs["config"] is configs[1]
assert build_config.call_count == 2
assert build_config.call_args.args[0] is argsAcceptance and preservation: for each entrypoint, two batches with the original args construct once; two further batches with replacement args bring the total to exactly two and receive the replacement config. Both phases must return the downstream result unchanged. Retain the existing packed-entrypoint assertions that explicit process groups and config are forwarded by identity, MTP selection respects vp_stage, and only Hybrid requests ContextParallelBatch.
Completed the light review of both changed files and their directly affected args-lifecycle, MTP, packing, and CP paths. No production correctness regression identified. Tests were not executed.
Review generated by codex. This was a full review. Request a full review with /review reset=true.
Add a regression test for pretrain_gpt.py and pretrain_hybrid.py that calls get_batch repeatedly and checks the config is built once per args object, and rebuilt when args is replaced by an equal but distinct object. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Deepak Narayanan <dnarayanan@nvidia.com>
|
/review |
svcnvidia-nemo-ci
left a comment
There was a problem hiding this comment.
LGTM. The prior coverage finding is resolved by the new parameterized regression. For both entrypoints, it checks one config construction across repeated calls, rebuilding when args is replaced by an equal-valued but distinct object, config forwarding by identity, and unchanged downstream return identity.
Completed the light follow-up against the previous reviewed head, including the new test’s integration and isolation. The existing process-group, virtual-stage MTP, and Hybrid return-interface assertions remain intact. No unresolved prior finding or qualifying introduced regression identified.
Source inspection only; tests were not executed.
Note: formal approval is not available for this request, so this review is posted as a comment only.
Review generated by codex. This was a findings-only review. Request a full review with /review reset=true.
|
🔄 Merge queue validation started! You can track the progress here: https://github.com/NVIDIA/Megatron-LM/actions/runs/40701164380 |
|
🔄 Merge queue validation started! You can track the progress here: https://github.com/NVIDIA/Megatron-LM/actions/runs/40716203359 |
Single repair commit for the main slice 5812485 -> dev merge. - ssm/kda: restore dev's `False` distribute_saved_activations argument in both KDA recompute checkpoints. The kda.py conflict resolution had taken main NVIDIA#7054's config read; dev's recompute contract (NVIDIA#8203, test_gdn_quantized_recompute) passes False, as dev's GDN does. - tests/models/test_hybrid_mhc: main NVIDIA#7555 replaced mtp_layer_pattern with mtp_layer_config_list; assert the new attributes instead. - tests/transformer/moe/test_route_metadata_cpu: the dispatcher test double accepts packed_seq_params, which main NVIDIA#8458 now passes. - tests/transformer/test_attention_output_gate: the KDA test double sets two_stage_gates=False, read by main NVIDIA#7054's _apply_gated_norm. - tests/data/test_get_batch: stub dev's prepare_packed_seq_params hook in main NVIDIA#8525's config-cache test; all main assertions are kept. - ssm/kda: keep dev's direct-gate op order in _apply_gated_norm (norm, then gate reshape). main NVIDIA#7054 reshaped the gate first, which changed the compiled rounding that dev NVIDIA#8006's test_kda_gated_norm_preserves_upstream_ rounding pins; main's two-stage branch is unchanged. - tests/data/test_pretrain_gpt_dataset_config: main NVIDIA#8391 replaced pretrain_gpt's build_tokenizer(args) with get_tokenizer(); the dev test now patches get_tokenizer. Its assertion is unchanged. - ssm/kda_layer_config: run validate_kda() before TransformerConfig's __post_init__ as well as after it. Dev NVIDIA#6556's shared GDN-family assertions ran first and preempted the KDA ValueErrors that main NVIDIA#7054's test_kda config tests expect. Dev's plain TransformerConfig checks are unchanged. - tests/transformer/test_thd_cuda_graph: the dev HybridModel test double sets hybrid_layer_config_list=None, which main NVIDIA#7555 reads in HybridModel.forward. Main NVIDIA#7555 made the same change to its own hybrid test doubles. The test assertions are unchanged. - functional goldens: refresh dev_dgx_h100 golden values for the four gpt3_mcore_te_tp2_pp2 mHC cases after main NVIDIA#7521 changed their numerics (local H100 runs on merge 44a8771). - tests/unit_tests/ssm/test_kda.py: keep dev NVIDIA#6556 kda_safe_gate/kda_lower_bound on TransformerConfig (CLI flags); main NVIDIA#7054 placement check now excludes only those two fields and asserts they stay on the stack config. - tests/unit_tests/ssm/test_kda.py: main NVIDIA#7054's recompute-acceptance test builds a non-hybrid experimental_attention_variant="kda" config. Dev NVIDIA#6556 registers kda as a GDN-family variant, and dev requires linear_attention_freq for every standalone GDN-family model, so the kda case now passes linear_attention_freq=[1]. Dev's validation and the test assertion are unchanged. - docker: pin the ARM64 MoK build to main NVIDIA#8210's 7a8cfb7f. The merged MoK backend passes shared_output_gate_weight to mok.functional.forward, and main's test_mok_numerics runs that path on GB200. Dev's a2ad2d0c pin does not accept the argument (GB200 CI TypeError in test_real_mok_ddp_numerics). 7a8cfb7f is a fast-forward of a2ad2d0c (2 commits) that already contains both hunks of dev's docker/patches/mok.patch, so the patch, its bind mount and the install_mok.sh patch argument are removed as on main, and the MoE README names the new snapshot. The AMD64 (H100) image still skips MoK; the rest of dev's Dockerfile.ci.dev is unchanged. - .github/workflows/cicd-main.yml: NVIDIA-NeMo/RL 4aaa48fa (NVIDIA#4253) renamed tests/functional/L1_Functional_Tests_Megatron_4.sh to L1_Functional_Tests_Megatron_Generation.sh, so the NeMo RL dispatch pattern matched no script and the downstream pre-flight failed with "No test scripts match the requested selection". Point the pattern at the renamed script. Dev and main carry the same stale pattern. Signed-off-by: svcnemo-autobot <svcnemo-autobot@nvidia.com>
Summary
get_batchinpretrain_gpt.pyandpretrain_hybrid.pyrebuilt the transformer config from args on every microbatch viacore_transformer_config_from_args. That re-runsTransformerConfig.__post_init__each time, so its validation warnings repeat in the log every step. For example, with--fp32-residual-connectionthe rank-0 log getsfp32_residual_connection is enabled, overriding pipeline_dtype from torch.bfloat16 to torch.float ...once per microbatch.This caches the config per args object (
_get_transformer_config(args)), so it is built once per training run and rebuilt only when the global args object is replaced. The fieldsget_batchreads (pipeline layout, MTP layer count, virtual pipeline size, tensor-parallel size and sequence parallelism, CP layouts) are fixed for the run.The cache is keyed on the args object rather than being a bare
lru_cache, sotests/unit_tests/data/test_get_batch.py, which builds fresh args per test and patchescore_transformer_config_from_args, still gets a config built from each test's own args.Testing
Not yet run;
tests/unit_tests/data/test_get_batch.pyis the relevant suite. Both changed files passpython -m py_compile.🤖 Generated with Claude Code