Skip to content

fix(fsdp): run torch_musa's FSDP2 replacements only on MUSA meshes - #126

Closed
yeahdongcn wants to merge 1 commit into
mainfrom
xd/fsdp-cpu-mesh
Closed

yeahdongcn wants to merge 1 commit into
mainfrom
xd/fsdp-cpu-mesh

Conversation

@yeahdongcn

Copy link
Copy Markdown
Collaborator

What / Why

torch_musa replaces parts of FSDP2 at import time with versions that create MUSA streams whatever the device mesh is. fully_shard on a CPU (gloo) mesh therefore fails in the root pre-forward under torch_musa:

  • with MUSA devices visible, a MUSA stream waits on a CPU stream: AttributeError: 'NoneType' object has no attribute 'wait';
  • with MUSA_VISIBLE_DEVICES=, the communication-context setup calls torch.musa.current_stream(): RuntimeError: No MUSA GPUs are available.

PyTorch runs the same CPU-mesh FSDP2 without error. vLLM-Omni's MAGI-2 test_compiled_regions_match_eager_under_dlo_and_hsdp (HSDP on a 4-rank gloo CPU mesh) fails this way on MUSA.

Change

New src/torchada/_fsdp2.py, installed by _patch_fsdp2_device_dispatch in _patch.py:

  • torch.distributed.fsdp.fully_shard sends a non-MUSA mesh straight to PyTorch's fully_shard; MUSA meshes and mesh=None keep torch_musa's wrapper.
  • FSDPCommContext.lazy_init, FSDPParamGroup.wait_for_unshard and post_backward, and FSDPState._root_post_backward_final_callback dispatch per call: a musa device runs torch_musa's function, any other device runs PyTorch's. The torch_musa module globals that hold these functions are the same dispatchers, so the methods still pickle by reference.
  • PyTorch's methods that torch_musa replaces at import are rebuilt from the installed PyTorch source after checking that it matches the loaded code. If any of them cannot be recovered, nothing is installed and torch_musa's FSDP2 stays as it is, with one warning.
  • A warning names any other method of these classes that torch_musa replaces (it still runs for every mesh), and a fully_shard attribute that is neither torch_musa's wrapper nor PyTorch's and so is not routed. A reference to torch_musa's wrapper bound before import torchada is not routed.

MUSA meshes keep running torch_musa's functions, with its streams, overlap level and collectives. Nothing is imported that torchada would not already import.

Verification

Tests: tests/test_fsdp2_device_dispatch.py, with a fake torch_musa FSDP2 layer (tests/_fsdp2_fakes.py) and a multi-process worker (tests/_fsdp2_mesh_worker.py):

  • on any host, the dispatch, recovery, idempotency, fail-closed and pickling behaviour, and 2- and 4-rank gloo runs whose outputs and FSDP2 call traces match plain PyTorch;
  • on a MUSA host, the same CPU-mesh runs against the real torch_musa (devices visible and hidden), and MUSA-mesh runs (musa and cuda meshes, 1-D and 1x2, default, overlap level 4 and both custom collective types) that must match torch_musa without this change. A MUSA-mesh comparison is skipped, naming the configuration and torch_musa's error or timeout, where torch_musa alone cannot complete it.

Results on MTT S5000 (torch 2.11.0.post2, torch_musa 2.11.0.post2+musa5.2.0, 2 cards):

Not covered / notes

  • Only torch 2.11 was run.
  • ShardedGradScaler on a CPU mesh is still torch_musa's version.
  • The version is not bumped here.

AI assistance: Claude Code drafted the change, the tests and this description and ran the MUSA validation listed above.

torch_musa replaces parts of FSDP2 with versions that create MUSA streams
whatever the device mesh is, so fully_shard on a CPU (gloo) mesh fails in
the root pre-forward: with MUSA devices visible a MUSA stream waits on a CPU
stream ('NoneType' object has no attribute 'wait'), and with
MUSA_VISIBLE_DEVICES= the comm-context setup raises "No MUSA GPUs are
available". PyTorch runs the same CPU-mesh FSDP2 without error.

Route torch_musa's FSDP2 replacements by device type:

- torch.distributed.fsdp.fully_shard sends a non-MUSA mesh straight to
  PyTorch's fully_shard; MUSA meshes and mesh=None keep torch_musa's
  wrapper. A reference to torch_musa's wrapper bound before
  `import torchada` is not routed.
- FSDPCommContext.lazy_init, FSDPParamGroup.wait_for_unshard and
  post_backward, and FSDPState._root_post_backward_final_callback dispatch
  per call: a "musa" device runs torch_musa's function, any other device
  runs PyTorch's. The torch_musa module globals that hold these functions,
  including the ones its lazy FSDP2 setup assigns from, are the same
  dispatchers, so the methods pickle by reference as torch_musa's do.
- PyTorch's methods that torch_musa replaces at import are rebuilt from
  the installed source after checking that it matches the loaded code. If
  any cannot be recovered, nothing is installed and torch_musa's FSDP2
  stays in place.
- A warning names any other method of these classes that torch_musa
  replaces, which still runs for every mesh, and a fully_shard attribute
  that is not torch_musa's wrapper and so is not routed.

MUSA meshes keep running torch_musa's functions, with its streams, overlap
level and collectives.

The tests run a fake torch_musa FSDP2 layer on CPU, including multi-process
gloo runs whose results and FSDP2 call traces match PyTorch's, and add
MUSA-host tests against the real torch_musa. A MUSA-mesh comparison is
skipped, naming the configuration and torch_musa's error or timeout, where
torch_musa alone cannot complete it.

Signed-off-by: Xiaodong Ye <xiaodong.ye@mthreads.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant