Skip to content

Add a strict mode that turns process-group fallbacks into errors - #8733

Closed
Connor-XY wants to merge 60 commits into
NVIDIA:mainfrom
Connor-XY:yx/pstate-s1-strict-fallbacks
Closed

Connor-XY wants to merge 60 commits into
NVIDIA:mainfrom
Connor-XY:yx/pstate-s1-strict-fallbacks

Conversation

@Connor-XY

@Connor-XY Connor-XY commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Depends on #8049, which contains #6293. Please review the diff from the merge of #8049 into main: scoped diff.

Why. Megatron Core's fallbacks to the global process groups emit ProcessGroupFallbackWarning (#8049), but a run cannot prove that it never reaches one:

  • A bare simplefilter("error", ...) misses a fallback that already warned, because each fallback warns once per process.
  • python -W and PYTHONWARNINGS cannot name the class. Python resolves them before torch can be imported.
  • suppress_duplicate_logs_off_rank0 ignores FutureWarning off rank 0, so only rank 0 reports fallbacks, although ranks can fall back differently, for example per pipeline stage. Its filters were prepended, so they also beat an error filter installed earlier. Rank 0 then raised while the other ranks went on and waited at their next collective.
  • Without an initialized grid, resolve_process_groups(None, ...) returns None groups, and torch.distributed treats None as WORLD. The warning did not say so.

What.

  • megatron/rank_log_setup.py: the rank-0 suppression appends its filters, so earlier filters keep precedence. ProcessGroupFallbackWarning passes both its filters and its showwarning override. The module stays torch-free: it matches the class by module and qualified name through an ABC __subclasshook__.
  • enable_strict_process_group_fallbacks() in megatron.core.process_groups_config puts an error filter for the class at the front of the filter list and forgets which fallbacks already warned. Every later fallback then raises on the rank that reaches it. Other warnings are unaffected, and warnings.catch_warnings() scopes it. Megatron-Bridge and NeMo-RL can call it too.
  • --strict-process-group-fallbacks makes initialize_megatron call it right after logging setup, before distributed initialization, seeding and model construction.
  • Without a grid, resolve_process_groups still builds the collection from the accessors. It does not raise, because code that works without a grid on main moves onto the resolver during the deprecation window: the FP8/FP4 contexts, the batch-invariant MoE unpermute, and callers that check the groups themselves. The warning now names the groups that are None and says that torch.distributed treats them as WORLD. It is recorded apart from the initialized-grid warning. An accessor that needs a grid, such as the one for dp, still raises. On an initialized grid nothing changes.
  • skills/mcore-testing/SKILL.md documents the unit-test opt-in, pytest -W error::megatron.core.process_groups_config.ProcessGroupFallbackWarning. pytest accepts the dotted class path.

No functional test enables the flag in this PR. #8777 turns it on in five functional tests and depends on this PR.

Tests.

  • test_rank_log_setup.py (CPU): fallback warnings show on ranks 0 and 1, also inside a library's catch_warnings; error filters for four categories, installed before and after the suppression; a pytest filterwarnings marker.
  • test_process_group_fallback.py:
    • the strict helper raises on every fallback, including one that already warned, and on nothing else;
    • it takes precedence over earlier filters, and raises on every rank after the rank-0 suppression, in one process and across all ranks;
    • the no-grid fallback names its None groups, keeps its own once-per-owner record, and still raises for dp.
  • New tests/unit_tests/training/test_strict_process_group_fallbacks.py: the flag parses, is applied after logging setup, and makes a fallback raise during distributed initialization and without a grid.

In the integration run (main 77391e7 with all 93 #6307 PRs and the 6 main-based gap fixes merged, against main itself), every test in these files passes on 4 GPUs: test_process_group_fallback.py passes 34 tests (the file is not on main), test_rank_log_setup.py passes 16 vs 5 on main, and training/test_strict_process_group_fallbacks.py passes 10 (a new file). None of these files shows a regression against main.

Earlier measurements on the code before the sync with main 77391e7: on 4 GPUs, an earlier version of this branch on #8049's previous head showed no regressions, and 7 of the 8 training-flag cases failed without the change and passed with it. The eighth runs without the flag and passes on both. The branch then ran on 4 CPU gloo ranks: no regressions against #8049's head, and the 36 new tests pass. Only the final state was tested; the intermediate commits were not run on their own. With the other process-group changes from #6307 applied:

  • the full unit test suite shows no regressions against main;
  • unit-test reruns under -W error::megatron.core.process_groups_config.ProcessGroupFallbackWarning pass;
  • 19 of 20 end-to-end training smoke configurations run under --strict-process-group-fallbacks with no fallback warning on any rank. The 20th needs Transformer Engine 2.19 or later and was skipped.

Compatibility.

  • The flag is off by default.
  • The rank-0 suppression now appends its filters. An earlier error filter (user -W, strict mode, pytest) therefore raises on every rank, not only on rank 0, and ProcessGroupFallbackWarning shows on every rank. A launch that installs default or always filters for these categories before the suppression now wins over it, and only the showwarning override hides those warnings. On a standard launch the same warnings stay hidden off rank 0.
  • rank_log_setup.py matches the class by module and qualified name. Renaming or moving ProcessGroupFallbackWarning requires updating it; a test catches a mismatch.
  • Add a dedicated fallback warning and process-group collection helpers #8049's resolver has no production callers yet. Callers that later move onto it and asserted on main without a grid get None groups and the warning instead: param_is_not_tensor_parallel_duplicate, model_parallel_cuda_manual_seed, ExtendedRMSNorm.sharded_state_dict, MegatronModule.sharded_state_dict, build_inference_pg_collection and CudaGraphManager. Strict mode turns each of these into an error.
  • Strict mode works through the filter list. A filter installed later that matches the class, such as simplefilter("ignore") or a library's catch_warnings() with simplefilter("always"), takes precedence, and the fallback then only warns. Frameworks should call the helper after their own warning setup. The warning is an Exception, so a broad except Exception around a fallback would swallow the strict error.
  • --yaml-cfg runs build their args from the YAML file alone, so those files must carry strict_process_group_fallbacks, as they already carry batch_invariant_mode.
  • Whichever of this PR and Seed the RNG from the model's process-group collection #8031 lands second combines the imports in megatron/training/initialize.py into from megatron.core.process_groups_config import (ProcessGroupCollection, enable_strict_process_group_fallbacks).
  • Needs determinism-exempt while Remove global process-group reads from Megatron Core #6293 is unmerged: the diff against main includes Remove global process-group reads from Megatron Core #6293's dot_product_attention.py and dsa.py. This PR's own diff touches no kernel file. The SKILL.md edit leaves skills/mcore-testing/skill.oms.sig stale, as earlier edits to that file did.

The branch was synced with main 77391e7 on 2026-11-01 by a merge commit that brings in #8049's updated head (no rebase, no force-push), so existing commits and review links stay valid. The merge had no conflicts. The process-group allowlist records the reads main added since this PR was written.

Part of #6307.

🤖 Generated with Claude Code

Connor-XY and others added 30 commits October 8, 2026 10:27
Signed-off-by: Yan Xu <yxu1@nvidia.com>
A collection without an embd field, or tied pipeline embedding/output stages
without a usable embedding group, is a caller error. Report it with
ValueError instead of an assert, which is skipped under python -O.

Signed-off-by: Yan Xu <yxu1@nvidia.com>
Six sites in megatron/core called parallel_state accessors even though the
correct group was already stored on the object (self.tp_group / self.cp_group).
Pure substitution: no signature changes, no new arguments, no caller impact.

  SelfAttention.run_realtime_tests       3 sites -> self.tp_group
  SelfAttention.get_query_key_value_tensors  1 site -> self.tp_group
  MultiTokenPredictionLayer              1 site -> self.tp_group
  LLaVAModel                             1 site -> self.cp_group

Global process-group reads in megatron/core: 163 -> 157.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
…scope

Two more sites where the correct group was reachable without any API change.

transformer_engine.py: in the cp_comm_type == 'a2a+p2p' branch the code read the
global hierarchical CP groups, even though the caller-supplied branch directly
above asserts hasattr(pg_collection, 'hcp') for exactly that case. The assert
was already guaranteeing the group the code then declined to use.

language_module.py: the word-embedding replica id derived its DP-CP rank from
the global grid while the sharded tensor nine lines later is built against
metadata['dp_cp_group']. Now both use the same group, so replica id and
sharding agree under a caller-supplied grid.

Global process-group reads in megatron/core: 157 -> 155.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
megatron/core/models/multimodal/context_parallel.py hard-coded the global CP
grid in 11 places, across module-level helpers with no group parameter. This is
the vision-encoder path, so it is exactly where an independent encoder grid
silently used the LLM's groups instead.

cp_group is now a required argument on the public helpers and is threaded
through the private ones and the autograd Function. get_pg_size/get_pg_rank
preserve the previous behaviour when CP is disabled (size 1, rank 0).

The call graph is closed inside this repo: LLaVAModel already holds
self.cp_group, and the only other callers are unit tests. The module is not
re-exported and has no users in Megatron-Bridge or nemo-rl.

Global process-group reads in megatron/core: 155 -> 144.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
_gather_from_tensor_parallel_region read the global TP grid for its size, rank
and both collectives. Both callers are subclasses that already hold the group
(self.tp_group on ColumnParallelLinear, self._tp_group on the TE variant), and
the mapping helpers it calls already accept an explicit group.

Global process-group reads in megatron/core: 144 -> 141.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
… in tests

Caught by a distributed run: changing the autograd Function signature
missed one direct .apply() call site in the unit tests.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
hybrid_context_parallel_forward_backward read the global TP and DP-CP grids for
its broadcast source rank, broadcast group, rank checks and barrier. Its only
caller is forward_backward_no_pipelining, which already holds pg_collection.

dp_cp is asserted in the hybrid-CP branch rather than unconditionally, matching
how TEDotProductAttention asserts hcp only for cp_comm_type == 'a2a+p2p'; the
legacy schedule collection already requests dp_cp.

get_tensor_model_parallel_src_rank() becomes
torch.distributed.get_global_rank(tp_group, 0), which is its definition.

hybrid_cp_schedule.py no longer imports parallel_state at all.

Global process-group reads in megatron/core: 141 -> 136.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
run_realtime_tests compared Q/K layernorm parameters across the global DP grid.
The DP group is required only by this debug check, so it is asserted there
rather than in __init__, which asks use_mpu_process_groups for tp/cp only --
the same idiom TEDotProductAttention uses for hcp.

megatron/core/transformer/attention.py no longer references parallel_state.

Global process-group reads in megatron/core: 136 -> 133.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
mtp_on_this_rank read the global PP rank to decide whether MTP layers live on
this stage. Both model callers (GPTModel, HybridModel) already hold self.pp_group
from LanguageModule.__init__, which runs before the call.

Global process-group reads in megatron/core: 125 -> 124.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
RotaryEmbedding and MultimodalRotaryEmbedding fell back to the global CP grid
via a ternary when cp_group was not passed. 10 of the 22 constructor calls in
this repo already passed one; the remaining 12 (3 in core, 9 in tests) now do
too, so the fallback is gone rather than merely unused.

megatron/core/models/common/embeddings/rotary_pos_embedding.py no longer
references parallel_state.

Global process-group reads in megatron/core: 124 -> 122.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
…nd T5Model

Both classes fell back to ProcessGroupCollection.use_mpu_process_groups() when
no collection was passed. The fallback is now an assertion naming the missing
argument, which is the 'fails loudly rather than silently' half of the goal.

DotProductAttention: already dead in-repo -- Attention always passes
pg_collection when building core_attention, and the one direct test
construction passes it positionally.

T5Model: 4 of 5 constructors did not pass one (3 tests, 1 example); they now do.

This is a deliberate breaking change for external callers that construct these
directly without a collection, which is the intent of the migration.

Global process-group reads in megatron/core: 122 -> 120 (shims 20 -> 18).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
…lock

The use_mpu_process_groups fallback becomes an assertion naming the missing
argument. All 7 constructors without one live in a single test module and now
pass a collection.

Global process-group reads in megatron/core: 120 -> 119 (shims 18 -> 17).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
LLaVAModel is the case the migration exists for: a vision encoder and an LLM may
run on independent parallel grids, so falling back to the global grid silently
picks the wrong one. The fallback becomes an assertion that says exactly that.

All 8 constructors without a collection (7 tests, 1 example) now pass one.

Global process-group reads in megatron/core: 119 -> 118 (shims 17 -> 16).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
Attention, MultiLatentAttention (x2) and AbsorbedMLASelfAttention each fell back
to use_mpu_process_groups when no collection was passed. All are dead in-repo:
TransformerLayer sets attention_optional_kwargs['pg_collection'] unconditionally
before build_module, and every direct constructor already passes one (verified
by AST across megatron/, tests/ and examples/).

Global process-group reads in megatron/core: 118 -> 114 (shims 16 -> 12).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
Global process-group reads in megatron/core: 114 -> 113 (shims 12 -> 11).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
These were based on a faulty inventory. My constructor scan filtered on
len(node.args) < 2, which silently skipped every call site passing arguments
positionally, so 'no caller is missing pg_collection' was wrong.

A paired run showed 85 real failures:
  63x AssertionError: MultiLatentAttention requires an explicit pg_collection
  22x AssertionError: Attention requires an explicit pg_collection
  10x TypeError: DSAIndexer.__init__() got multiple values for 'pg_collection'
      (my inserted keyword duplicated an existing positional argument)

Reverting to the last validated state. The earlier fallback removals
(DotProductAttention, T5Model, MultiTokenPredictionBlock, LLaVAModel) were each
validated against a pristine baseline and are retained.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
Redo of the reverted attempt, this time with a signature-aware inventory: both
DSAIndexer constructors already pass pg_collection positionally, so the fallback
is dead and the tests need no change. The previous attempt added a redundant
keyword and produced 'got multiple values for argument pg_collection'.

Global process-group reads in megatron/core: 118 -> 117 (shims 16 -> 15).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
Second attempt, this time driven by the signature-aware scanner: 15 of 16
SelfAttention constructors were missing pg_collection (all in test modules) and
now pass one. The earlier attempt used a scan that skipped positional call
sites and had to be reverted.

Global process-group reads in megatron/core: 117 -> 116 (shims 15 -> 14).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
…sses

Second attempt, driven by the signature-aware scanner. MultiLatentAttention (x2)
and AbsorbedMLASelfAttention no longer fall back to use_mpu_process_groups.
18 of 19 MLASelfAttention constructors were missing pg_collection, all in one
test module, and now pass one; AbsorbedMLASelfAttention's single constructor
already did.

Global process-group reads in megatron/core: 116 -> 113 (shims 14 -> 11).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
Paired run: 24 failed / 43 passed against a pristine 20 / 57, with 110
'requires an explicit pg_collection' assertions. The failures are concentrated
in TestFusedMLA*, i.e. a MultiLatentAttention subclass my scan never covered --
I scanned MLASelfAttention and AbsorbedMLASelfAttention by name and assumed
those were the only constructors reaching the base __init__.

The Attention removal in the preceding commit validated clean (5F/121P on both
trees) and is retained.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
…sses

Third attempt. The previous two failed because my inventory was incomplete: the
first scan skipped positional call sites, the second scanned MLASelfAttention
and AbsorbedMLASelfAttention by name and missed FusedMLASelfAttention, which
also reaches MultiLatentAttention.__init__ (110 assertion failures, all in
TestFusedMLA*).

This time the subclass list was derived programmatically from the class
hierarchy: MLASelfAttention (18 sites), FusedMLASelfAttention (6 sites),
AbsorbedMLASelfAttention (0). All now pass a collection.

Global process-group reads in megatron/core: 116 -> 113 (shims 14 -> 11).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
The only construction path is GroupedMLP.__init__ (experts.py), which
dereferences pg_collection.ep and .expt_tp unconditionally before passing it on
-- so it cannot be None there. No direct constructors exist anywhere in the
repo.

Global process-group reads in megatron/core: 113 -> 112 (shims 11 -> 10).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
dynamic_engine and abstract_model_inference_wrapper each derived a collection
from global state when the inference config carried none. Both now call
InferenceConfig.resolve_pg_collection(), so the fallback exists once at the
config boundary and consumers can assume a collection.

Resolution is lazy rather than in __post_init__, since configs are commonly
built before initialize_model_parallel.

Also drops a stale comment in attention.py referring to a fallback that no
longer exists.

Global process-group reads in megatron/core: 110 -> 109 (shims 10 -> 9).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
…ries

Two independent reductions of the same shape as the MoE one: many global reads
become one, with no signature or caller changes.

distributed_trtllm_model_weights_converter: five parallel_state reads (pp size,
tp size, tp rank, pp rank, tp group) become one use_mpu_process_groups call with
sizes and ranks derived from it. The file no longer imports parallel_state.

inference/communication_utils: broadcast_tensor fetched the model-parallel
source rank and the group separately; the source rank is rank 0 of that group,
so one lookup gives both. broadcast_list called get_model_parallel_src_rank
twice for the same value.

Global process-group reads in megatron/core: 102 -> 96 (accessors 92 -> 85).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
Rebasing onto current main surfaced three FusedMLASelfAttention constructors
added since this work began. They reach MultiLatentAttention.__init__, which
this branch makes require an explicit collection.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
Rebasing onto main (c9b53d0) brought in nine constructor calls written
against the old fallbacks, which this series has removed:

- YarnRotaryEmbedding x2 in test_mla_yarn_rope_apply.py: cp_group
- SelfAttention and RotaryEmbedding in test_attention_packed_seq.py:
  pg_collection / cp_group (the build-time CP group the test already reads)
- MLASelfAttention in test_multi_latent_attention.py: pg_collection
- MultiTokenPredictionBlock x4 in test_multi_token_prediction.py: pg_collection
  with cp/tp/pp, since the block now dereferences pg_collection.pp as well

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
Two call sites the series left behind:

- pretrain_vlm.py built LLaVAModel without a collection although
  model_provider already accepts pg_collection. Resolve the global-grid
  fallback at the script boundary and forward it, as
  examples/multimodal/model.py does.
- The hybrid CP scheduler tests monkeypatched
  hybrid_cp_schedule.parallel_state, which the scheduler no longer imports.
  Hand them a fake pg_collection with tp and dp_cp groups instead and pass
  it through; the TP broadcast source now comes from
  torch.distributed.get_global_rank, so map that to the group rank too.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
TEGroupedLinear now requires an explicit process-group collection. The
mixed-split determinism test builds one directly; pass the global collection
there as the other constructions in this file do.

Signed-off-by: Yan Xu <yxu1@nvidia.com>
@copy-pr-bot

copy-pr-bot Bot commented Oct 30, 2026

Copy link
Copy Markdown

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.

Connor-XY and others added 5 commits October 30, 2026 13:13


The strict mode that follows turns NVIDIA#8049's ProcessGroupFallbackWarning
into an error, extends its resolve_process_groups and
require_process_groups helpers, and changes the fallback warning that
NVIDIA#8049 carries from NVIDIA#6293, so merge NVIDIA#8049 here. NVIDIA#8049's head already
contains main, so the merge is clean and its tree is NVIDIA#8049's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
suppress_duplicate_logs_off_rank0 prepends ignore filters for
UserWarning, FutureWarning and DeprecationWarning on every rank but
rank 0, and drops the same categories in a showwarning override. Two
problems follow:

- ProcessGroupFallbackWarning is a FutureWarning, so only rank 0
  reports it. Ranks can fall back to the global process groups
  differently, for example on different pipeline stages, so the other
  ranks' fallbacks are lost.
- The ignore filters take precedence over an "error" filter installed
  before the call, by the user, a strict mode or pytest's -W. Rank 0
  then raises while the other ranks go on and wait for it at their
  next collective.

The suppression now appends its filters, so filters installed earlier
keep precedence; filters installed after the call already did. On a
standard launch the only earlier filters for these categories are
Python's defaults for DeprecationWarning, and the showwarning override
still drops what they show, so the same warnings stay hidden.
ProcessGroupFallbackWarning passes both the filters and the
showwarning override. The module must not import torch, so it matches
the class by module and name through an ABC __subclasshook__ instead
of importing it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
ProcessGroupFallbackWarning reports each place where a component falls
back to the global process groups in parallel_state. Making those
fallbacks fatal, for example in CI, needs more than a bare
warnings.simplefilter("error", ProcessGroupFallbackWarning):

- A fallback warns once per owner and argument per process. One that
  warned before the filter was installed returns early and never
  raises.
- python -W and PYTHONWARNINGS cannot name the class, so callers need an
  in-code entry point that Megatron-LM training, Megatron-Bridge and
  NeMo-RL can share.

enable_strict_process_group_fallbacks() installs the error filter at
the front of the filter list, ahead of earlier filters such as the
rank-0 log suppression, and forgets which fallbacks already warned, so
every later fallback raises on the rank that reaches it. Other
warnings are unaffected, and warnings.catch_warnings() scopes it.

The unit-test guide documents the pytest opt-in: pytest's -W and
filterwarnings accept the dotted class path, unlike python -W.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
A training run cannot show today that it passes its process groups
explicitly: each fallback to the global groups only warns once, and
the warning is easy to miss among hundreds of ranks. CI needs a run
that fails at the first fallback instead.

--strict-process-group-fallbacks makes initialize_megatron call
enable_strict_process_group_fallbacks() right after the logging setup.
The entry point's rank-0 log suppression ran earlier, on import, and
the strict filter takes precedence over it, so every rank that
reaches a fallback raises. The filter is in place before distributed
initialization, seeding and model construction. The flag is off by
default, so runs that omit it behave as before.

Stock training reaches fallbacks that later changes remove, so no
functional test enables the flag yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
When the caller omits its collection, resolve_process_groups builds
one through use_mpu_process_groups, which reads most groups with
check_initialized=False. If parallel_state is not initialized, those
groups come back as None, require_process_groups accepts None as an
axis that is off, and torch.distributed then treats each None group as
WORLD: a tensor-parallel all-reduce would sum over every rank. The
fallback warning did not say so.

Without a grid the fallback still builds the collection as before,
because code that works without a grid today moves onto the resolver
during the deprecation window:

- get_fp8_context, get_fp4_context and the Transformer Engine
  quantization contexts pass no amax group without a grid, which
  Transformer Engine reads as its default group, the right domain for
  pure data parallelism.
- The batch-invariant MoE unpermute runs in a single process without
  torch.distributed.
- Callers that check the returned groups themselves, such as the MoE
  default collection, get_ep_layer_offset and the TensorRT-LLM
  converter, raise their own, more specific errors.

The global accessors return None for most groups, and an accessor that
needs an initialized grid, such as the one for dp, still raises, so no
new None group reaches torch.distributed. A ProcessGroupFallbackWarning
now names the groups that are None and says that torch.distributed
treats a None group as the default (WORLD) group. It is recorded apart
from the warning for an initialized grid, so a process that reaches
both hears about both. Strict mode, through
enable_strict_process_group_fallbacks(),
--strict-process-group-fallbacks or pytest's -W error filter, turns it
into an error like any other fallback.

On an initialized grid nothing changes: the warning still comes before
any group is read, and its text is the same. An explicit collection is
returned as before, without reading parallel_state. The warning still
reaches every rank under the rank-0 log suppression.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
@Connor-XY
Connor-XY force-pushed the yx/pstate-s1-strict-fallbacks branch from 7fb6470 to 5cf3d3f Compare November 1, 2026 01:37
Connor-XY added a commit to Connor-XY/Megatron-LM that referenced this pull request Nov 1, 2026
The multimodal dataloader and example tests that follow call
process_groups_config.enable_strict_process_group_fallbacks(), which
NVIDIA#8733 adds, so that a fallback to the global grid fails the test instead
of warning. The merge is clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
Connor-XY added a commit to Connor-XY/Megatron-LM that referenced this pull request Nov 1, 2026
The multimodal dataloader provider test that follows calls
training._get_train_full_dataset_sample_count(args, provider,
pg_collection) and expects the provider to build its loader on the
run's collection. NVIDIA#8741 adds the pg_collection argument to the
full-dataset length probe, and NVIDIA#8757's commit that passes the run's
process groups to the probe hands it to the provider; that commit moved
from this branch into NVIDIA#8757.

megatron/training/initialize.py conflicts on the process_groups_config
import: NVIDIA#8733 imports enable_strict_process_group_fallbacks, NVIDIA#8031
(which NVIDIA#8757 brings through NVIDIA#8737) imports ProcessGroupCollection, and
NVIDIA#8757 adds warn_global_process_group_fallback. The import takes all
three.

The allowlist conflicts only on its total: 168 reads here and 158 in
NVIDIA#8757. It is regenerated from the merged tree, which records 154 reads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
Connor-XY added a commit to Connor-XY/Megatron-LM that referenced this pull request Nov 1, 2026
The accessor deprecation that follows adds global_process_group_fallback
next to the fallback warning helpers of NVIDIA#8049, which NVIDIA#8733 brings, and
extends NVIDIA#8733's strict-fallbacks section of the testing skill.

The merge conflicts only in the process-group usage allowlist, which is
regenerated on the merged tree.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
Connor-XY added a commit to Connor-XY/Megatron-LM that referenced this pull request Nov 1, 2026
The warned fallbacks outside Megatron Core that follow rewrite the
global-grid reads in checkpointing.py that NVIDIA#8758 warns, and NVIDIA#8758 brings
NVIDIA#8757's training-loop helpers that the training files use.

megatron/training/initialize.py conflicts on the process_groups_config
import: NVIDIA#8733 imports enable_strict_process_group_fallbacks, NVIDIA#8031
(which NVIDIA#8758 brings through NVIDIA#8737) imports ProcessGroupCollection, and
NVIDIA#8757 (which NVIDIA#8758 brings) adds warn_global_process_group_fallback. The
import takes all three.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
Connor-XY added a commit to Connor-XY/Megatron-LM that referenced this pull request Nov 1, 2026


This base holds every other PR of the parallel_state deprecation series
(NVIDIA#6307): the end-to-end test that follows asserts that no process-group
fallback or deprecation warning fires anywhere on a collection that is
not the global grid, and the strict-fallback functional tests turn
every remaining fallback into an error, so both need the whole series
below them. NVIDIA#8773, which now carries the checkpoint-converter test and
the review rules, brings NVIDIA#8772, NVIDIA#8770, NVIDIA#8768 and everything under them:
the NVIDIA#8765 -> NVIDIA#8766 -> NVIDIA#8767 deprecations, the fallback warnings in and
outside Megatron Core, the training-path changes and group fixes, NVIDIA#8747,
NVIDIA#8733 and the existing PRs they need.

Nine files conflict:
- The duplicated-mode linears of absorbed_mla.py (2),
  multi_latent_attention.py (3), dsa_gqa.py (2), csa.py (4),
  deepseek_v4_hybrid_attention.py (1), dsa.py (3) and moe_layer.py (2):
  NVIDIA#8722's eabb21e passes the owner's TP group (tp_group=...tp) where
  NVIDIA#8773 keeps the "None in duplicated mode" form, and NVIDIA#8748, which NVIDIA#8773
  brings, adds pg_collection=... next to it (2a712de, 8d0a49b).
  Each call keeps NVIDIA#8722's tp_group line and NVIDIA#8748's pg_collection line.
- parallel_state.py, get_data_parallel_group_gloo and
  get_expert_data_parallel_group_gloo: add NVIDIA#8765's @_deprecated_accessor
  decorators, which NVIDIA#8773 brings, above the merged signatures.
- The process-group usage allowlist is regenerated on the merged tree
  with NVIDIA#8773's checker (check_process_group_usage.py --update).
parallel_state.py equals NVIDIA#8773's base.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
Connor-XY and others added 3 commits November 1, 2026 09:00
Conflicts:
- examples/t5/pretrain_t5.py: Main adds `cfg = get_run_config()` to
  model_provider and drops the `build_tokenizer` import in favour of
  `get_tokenizer()`. The PR moves that import for isort and passes a
  ProcessGroupCollection into T5Model. The result keeps main's `cfg`
  line and the PR's pg_collection fallback and argument. It drops the
  `build_tokenizer` import, which would otherwise be unused.
- megatron/core/models/common/embeddings/rotary_pos_embedding.py: Main
  imports `register_capture_constant` for its shared rotary cache. The
  PR imports `warn_global_process_group_fallback`. Both imports are
  kept, along with main's cache refactor and the PR's warning
  cp_group fallback.
- pretrain_gpt.py, pretrain_hybrid.py: Main (NVIDIA#8341, NVIDIA#8592) moves
  `is_dataset_built_on_rank` into megatron/training/datasets/providers.py
  and the dataset config builder into argument_utils. The PR adds
  `pp_group` to the `mtp_on_this_rank` call in the local predicate. The
  result takes main's layout and keeps the PR's `pp_group` in get_batch.

Adapted to main outside the conflicts:
- megatron/training/datasets/providers.py: The PR's predicate edit is
  re-applied in main's shared `is_dataset_built_on_rank_explicit`. It
  passes `pp_group=mpu.get_pipeline_model_parallel_group()` to
  `mtp_on_this_rank`, so middle pipeline stages do not hit the
  fallback warning. The function's signature is unchanged.

The process-group allowlist records the reads main added since this PR
was written.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
The process-group allowlist records the reads main added since this PR
was written.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
The process-group allowlist records the reads main added since this PR
was written.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Yan Xu <yxu1@nvidia.com>
@Connor-XY

Copy link
Copy Markdown
Contributor Author

Superseded by #8895, which carries this change as commit 3611587 under the same title, with "Originally #8733." in its message. Closing so review happens in one place. The branch stays on the fork.

@Connor-XY Connor-XY closed this Nov 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant