Skip to content

Bugfix - Use torchrun --standalone for single-node torch.distributed runs - #839

Open
polarG wants to merge 2 commits into
mainfrom
dev/hongtaozhang/torchrun-standalone-port
Open

Bugfix - Use torchrun --standalone for single-node torch.distributed runs#839
polarG wants to merge 2 commits into
mainfrom
dev/hongtaozhang/torchrun-standalone-port

Conversation

@polarG

@polarG polarG commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Description

When the runner builds a torchrun command for a torch.distributed-mode benchmark with node_num == 1, it passes no rendezvous arguments at all. In that case torchrun falls back to its own default rendezvous, which binds a fixed port (MASTER_PORT=29500). If a previous distributed job's socket on that same port hasn't fully cleared the kernel's TIME_WAIT state yet, the next single-node job's torchrun fails to bind with:

DistNetworkError: ... port: 29500 ... EADDRINUSE

This was observed for model-benchmarks:resnet during a full sb run on an MI300X host (Ansible-level failure, before the benchmark executor even started, so no result appears in the results summary at all). This PR switches the single-node case to torchrun --standalone, which spins up its own local rendezvous on a randomly chosen free port instead of a fixed one, eliminating the port-reuse race entirely.

Why this wasn't caught earlier

This is not a new bug: the node_num == 1 → empty rendezvous-args code path has existed since the torch.distributed mode was first added to the runner in 2021 (#81). It never surfaced as a visible failure before because it's a timing-dependent race, not a deterministic defect in any specific benchmark:

  • superbench/config/amd_mi300.yaml shows gpt, bert, lstm, resnet, densenet, and vgg under model-benchmarks all share the identical default_pytorch_mode anchor (torch.distributed, proc_num: 8, node_num: 1), so every one of them goes through the exact same fixed-port code path — yet only resnet hit EADDRINUSE in the observed run, while gpt/bert/lstm passed. That rules out anything resnet-specific.
  • The failure depends on whether the previous job to use port 29500 released its socket (cleared TIME_WAIT) before the next single-node job attempts to bind. That gap is a function of exact benchmark scheduling/ordering and per-run timing, so it only manifests when two single-node jobs happen to land close enough together — which is why this only showed up on this particular full end-to-end 462-benchmark run and not in smaller/prior runs.

Major Revision

  • superbench/runner/runner.py (__get_mode_command): for torch.distributed mode with node_num == 1, pass --standalone instead of no rendezvous args. Multi-node path (--nnodes=$NNODES --node_rank=$NODE_RANK --master_addr=$MASTER_ADDR --master_port=$MASTER_PORT) is unchanged.

Minor Revision

  • tests/runner/test_runner.py: updated test_get_mode_command's expected single-node command string to include --standalone.

Testing

  • test_get_mode_command passes with the updated expectation.
  • Verified in-container on an 8x MI300X host: torchrun --nproc_per_node=2 (old default) always binds MASTER_PORT=29500; with --standalone, back-to-back single-node runs picked distinct random ports (45635, then 36937) with no EADDRINUSE.

Single-node runs passed no rendezvous args, so torchrun used the fixed default port 29500, causing transient EADDRINUSE collisions between consecutive distributed benchmarks (e.g. model-benchmarks:resnet). Use --standalone so torchrun binds a random free port. Multi-node path unchanged; runner unit test expectation updated.
Copilot AI review requested due to automatic review settings July 29, 2026 21:25
@polarG
polarG requested a review from a team as a code owner July 29, 2026 21:25
Copilot AI previously approved these changes Jul 29, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Ready to approve

The change is small, targeted to the reported failure mode, preserves the multi-node behavior, and the affected unit test expectation is updated accordingly.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Pull request overview

This PR fixes intermittent single-node torchrun failures in the runner’s torch.distributed mode by ensuring single-node runs use torchrun --standalone (random free rendezvous port) rather than relying on torchrun’s default rendezvous behavior (fixed MASTER_PORT=29500), which can race with TIME_WAIT and cause EADDRINUSE.

Changes:

  • Update SuperBenchRunner.__get_mode_command to emit torchrun --standalone when node_num == 1 for torch.distributed mode.
  • Keep the existing multi-node rendezvous argument path unchanged.
  • Update runner unit test expectations to include --standalone for the single-node case.
File summaries
File Description
superbench/runner/runner.py Adds --standalone to single-node torch.distributed torchrun command generation to avoid fixed-port reuse failures.
tests/runner/test_runner.py Updates test_get_mode_command expected command string for single-node torch.distributed runs to include --standalone.
Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 0
  • Review effort level: Low

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

@polarG polarG self-assigned this Jul 29, 2026
@polarG polarG added bug Something isn't working ROCm labels Jul 29, 2026
@codecov

codecov Bot commented Jul 29, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86.02%. Comparing base (67298ae) to head (3b59d5e).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #839   +/-   ##
=======================================
  Coverage   86.02%   86.02%           
=======================================
  Files         103      103           
  Lines        7950     7950           
=======================================
  Hits         6839     6839           
  Misses       1111     1111           
Flag Coverage Δ
cpu-python3.10-unit-test 70.88% <ø> (ø)
cpu-python3.12-unit-test 70.88% <ø> (ø)
cpu-python3.7-unit-test 70.31% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

torch pulls in an unpinned setuptools during 'pip install .[test,cpuworker]',
which upgrades the 65.7 pinned by the pipeline to 83.0.0 and makes the
setup.py guard raise VersionConflict on the python-3.10 lint job.
setuptools 66+ only drops Python 3.7, so keep the cap there only.
Copilot AI review requested due to automatic review settings July 31, 2026 17:30
Copilot AI dismissed their stale review, a newer Copilot review was requested July 31, 2026 17:33

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Ready to approve

The changes are small, targeted, and the updated behavior is covered by an existing unit test that was updated to match the new command.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

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

Labels

bug Something isn't working ROCm

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants