Skip to content

fix(logging): use RANK env var in log_rank_zero before dist init - #2964

Open
kratos0718 wants to merge 1 commit into
meta-pytorch:mainfrom
kratos0718:fix/log-rank-zero-before-dist-init
Open

kratos0718 wants to merge 1 commit into
meta-pytorch:mainfrom
kratos0718:fix/log-rank-zero-before-dist-init

Conversation

@kratos0718

Copy link
Copy Markdown

Fixes #2700

Summary

log_rank_zero fell back to rank=0 for all processes when
dist.is_initialized() was False (i.e. before setup() calls
init_process_group). This meant config.log_config — called in
recipe_main before the recipe is constructed — printed the full
config on every rank in a multi-GPU torchrun launch.

torchrun always sets RANK in the environment before spawning
workers, so we use it as the fallback rank when the process group is
not yet initialized.

What changed

  • torchtune/utils/_logging.py: log_rank_zero now reads os.environ["RANK"] (default "0") as fallback instead of assuming 0
  • tests/torchtune/utils/test_logging.py: 3 new test cases covering pre-init RANK=0, RANK=1, and no-RANK (single device)

Impact

Fixes redundant config logging in all distributed recipes
(full_finetune_distributed, lora_finetune_distributed,
knowledge_distillation_distributed, etc.) with a single change.

Before torch.distributed is initialized (e.g. in recipe_main before
setup() calls init_process_group), dist.is_initialized() returns False
and the previous fallback was rank=0 unconditionally. This caused
config.log_config — and any other log_rank_zero call made before
distributed setup — to print on every rank in a torchrun launch.

torchrun always sets the RANK environment variable before spawning
worker processes, so we can use it as a reliable fallback to determine
the true rank before the process group exists.

Fixes meta-pytorch#2700

Signed-off-by: Abhinav Tarigoppula <[email protected]>
@meta-cla

meta-cla Bot commented May 22, 2026

Copy link
Copy Markdown

Hi @kratos0718!

Thank you for your pull request and welcome to our community.

Action Required

In order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you.

Process

In order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA.

Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with CLA signed. The tagging process may take up to 1 hour after signing. Please give it that time before contacting us about it.

If you have received this in error or have any questions, please contact us at [email protected]. Thanks!

@kratos0718 kratos0718 closed this May 22, 2026
@kratos0718

Copy link
Copy Markdown
Author

 ̑

@kratos0718 kratos0718 reopened this May 22, 2026
@meta-cla

meta-cla Bot commented May 22, 2026

Copy link
Copy Markdown

Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks!

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label May 22, 2026
@kratos0718

Copy link
Copy Markdown
Author

Hi! Following up — the Meta CLA is signed and all checks are passing. Happy to address any feedback or requested changes. Thanks!

@n-dlms n-dlms left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified the root cause and the fix against current main. The chain is: recipes call config.log_config in recipe_main (for example full_finetune_distributed.py L1161) before init_process_group, config.log_config routes through log_rank_zero (config/_utils.py L28), and the primitive on main falls back to rank=0 whenever dist.is_initialized() is False (_logging.py L102). So under torchrun every rank is rank zero at that point and all of them log the full config. The RANK env fallback is the right fix: torchrun exports RANK before the process group exists, the pre-init path now reflects the real rank, and the no-RANK single-device case still logs.

Two observations from reading the rest of the call graph. First, this fixes more than the config log: log_rank_zero is called directly by a number of distributed recipes (lora_dpo, qat, full_dpo, lora_finetune, knowledge_distillation, full_finetune, qat_lora), so any pre-init call site inherits correct behavior with no per-recipe edits. Second, get_world_size_and_rank (training/_distributed.py L313) keeps its (1, 0) pre-init fallback, which is fine since recipes only call it after init_process_group, but it does mean this becomes the first env-based rank reader in the codebase; there is no existing helper to reuse and no duplication introduced.

The tests cover the three meaningful states (RANK=0 logs, RANK=1 stays silent, RANK absent logs) with dist mocked as available but not initialized, which matches the real pre-init condition. One edge examined rather than skipped: RANK set to an empty string makes int("") raise ValueError. torchrun never produces that, and failing loudly on a malformed hand-set RANK is better than silently defaulting to zero, so I would not change it.

From my side this is ready for approval, and it is the minimal root fix for #2700.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Log config to output only on rank zero

2 participants