Skip to content
Merged
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ The platforms and engines in this repository are **reference implementations**
| Enflame GCU | GCU | ECCL / FlagCX | ✅ Example (requires vendor support) | [User Guide](docs/user_guide_enflame/README.md) |
| Huawei NPU | Ascend 910B | HCCL | Built-in (verl core) | [Ascend Tutorial](https://github.com/verl-project/verl/tree/main/docs/ascend_tutorial) |
| Iluvatar | BI-V150 (CUDA-compatible) | IXCCL | ✅ Supported | [User Guide](docs/user_guide_iluvatar/README.md) |
| Moore Threads | MUSA (CUDA-compatible) | MCCL | ✅ Supported | [User Guide](docs/user_guide_musa/README.md) |


## Installation
Expand Down Expand Up @@ -89,6 +90,7 @@ Each hardware platform provides a standalone user guide (following the structure
- **[MetaX GPU](docs/user_guide_metax/README.md)** — MetaX GPU user guide
- **[FlagOS](docs/user_guide_flagos/README.md)** — FlagOS unified heterogeneous engine user guide ([NVIDIA](docs/user_guide_flagos/nvidia/README.md))
- **[Enflame GCU](docs/user_guide_enflame/README.md)** — Enflame GCU user guide
- **[Moore Threads GPU](docs/user_guide_musa/README.md)** — Moore Threads GPU user guide

### Developer Guides

Expand Down
57 changes: 57 additions & 0 deletions docs/user_guide_musa/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# VERL MUSA User Guide

## Introduction

This document describes how to use verl for reinforcement learning training on
Moore Threads MUSA accelerators.

## Directory Structure

```text
verl_hardware_plugin/
├── engines
│ ├── fsdp_musa.py # FSDP engine support
│ └── megatron_musa.py # Megatron engine support
└── platforms
└── platform_musa.py # MUSA platform settings
```

```text
user_guide_musa/
├── README.md # This file
├── install_guidance.md # Installation and environment setup
└── quick_start.md # GSM8K GRPO quick start
```

## Getting Started

- [Installation Guide](./install_guidance.md) — prerequisites and environment setup
- [Quick Start](./quick_start.md) — run a GSM8K GRPO training job

## Platform Summary

| Item | Description |
|------|-------------|
| Device type | `musa` |
| Vendor identifier | `moore_threads` |
| Communication backend | `mccl` |
| Device visibility env var | `MUSA_VISIBLE_DEVICES` |
| Ray resource name | `GPU` |
| IPC support | Yes |

## MUSA Migration Patches

MUSA deployments may use two separate compatibility layers:

- MUSA support for the upstream Megatron/MCore implementation is provided by
the external `megatron-lm-musa-patch` compatibility layer. The patch is loaded
at runtime from the directory specified by `MUSA_PATCH_PATH` (usually
`/home/megatron-lm-musa-patch` in the release image); it adapts the
unmodified Megatron code for MUSA execution.

- MUSA compatibility for VERL and SGLang runtime components is provided by the
deployment-specific `verl-musa-patch` compatibility layer. The patch is loaded
at runtime from the directory specified by `VERL_MUSA_PATCH` (usually
`/home/verl-musa-patch` in the release image) and made available to Ray workers
through `PYTHONPATH`; it adapts the VERL and SGLang runtime components for MUSA
execution.
81 changes: 81 additions & 0 deletions docs/user_guide_musa/install_guidance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# MUSA Installation Guide

## Prerequisites

- A MUSA Docker image with the matching driver/runtime, `torch_musa`, MCCL,
SGLang, and other MUSA dependencies.
- Network access to download models and datasets.
- A VERL checkout and this plugin checkout.

The standard MUSA images already include SGLang and the external Megatron-LM
MUSA patch (usually `/home/megatron-lm-musa-patch`). Other runtime dependencies
such as Ray are also normally pre-installed. Do not add CUDA versions of these
packages, as they may override the MUSA packages.

## 1. Start the MUSA Docker Image
Comment thread
physics31415926 marked this conversation as resolved.

Use the MUSA release image provided for your hardware. The exact image name and
device mounts depend on the driver release; the following is a generic example:

```bash
docker_image="${MUSA_DOCKER_IMAGE:-}"
docker_name="${MUSA_DOCKER_NAME:-verl_musa}"

docker container create \
--name "${docker_name}" \
--privileged \
--net host \
--pid=host \
--shm-size 100g \
--ulimit memlock=-1 \
-v /home:/home \
-it \
"${docker_image}" \
/bin/bash

docker start -ai "${docker_name}"
```

Inside the container, verify that the pre-installed components are available:

```bash
ls /home
python3 -c 'import torch; import sglang; print(torch.musa.is_available())'
```

A public image is:

`registry.mthreads.com/mcctest/training-suite:v2.1.7.rc3-ut-verify`

## 2. Install verl and verl-hardware-plugin

```bash
# Install verl
git clone https://github.com/verl-project/verl.git
cd verl
pip install -e .

# Install verl-hardware-plugin
git clone https://github.com/verl-project/verl-hardware-plugin.git
cd verl-hardware-plugin
pip install -e .
```

## 3. Prepare Data and Models

The baseline scripts use Qwen3-0.6B and GSM8K. Set `MODEL_DIR` and `DATA_DIR`
to the paths available in your environment, for example:

```text
MODEL_DIR=/ipfs/models/Qwen/Qwen3-0.6B
DATA_DIR=/ipfs/models/gsm8k
```

## 4. Verify the Environment

```bash
python3 -c 'import torch; print(torch.musa.is_available(), torch.musa.device_count())'
```

The output should show that MUSA is available and report the visible device
count. Then follow the [Quick Start](./quick_start.md) to run a VERL script.
108 changes: 108 additions & 0 deletions docs/user_guide_musa/quick_start.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# MUSA Quick Start

This guide walks you through the GSM8K GRPO baseline on Moore Threads MUSA.
Complete the [Installation Guide](./install_guidance.md) first.

**Baseline scenario:** Qwen3-0.6B + GSM8K + FSDP actor + SGLang rollout — see
[`scripts/baseline_grpo_gsm8k.sh`](../../scripts/baseline_grpo_gsm8k.sh).

## 1. Prepare Data and Model

The MUSA image normally provides the runtime dependencies. Set the model and
dataset directories to paths available in your environment:

```bash
MODEL_DIR=/ipfs/models/Qwen/Qwen3-0.6B
DATA_DIR=/ipfs/models/gsm8k
```

## 2. Run the Baseline

From the repository root:

```bash

export VERL_PLATFORM=musa
export VERL_USE_EXTERNAL_MODULES=verl_hardware_plugin
export VERL_MUSA_PATCH=/home/verl-musa-patch
export RAY_EXPERIMENTAL_NOSET_MUSA_VISIBLE_DEVICES=1
export MUSA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7
export RAY_ACCEL_ENV_VAR_OVERRIDE_ON_ZERO=0
export MCCL_LIB=/usr/local/musa/lib/libmccl.so
export LD_LIBRARY_PATH="/usr/local/musa/lib:${LD_LIBRARY_PATH:-}"
export VLLM_PATCH_MUSA_CUSTOM_OPS=1
export SGLANG_MUSA_GRAPH_COMPAT=1
export PYTHONPATH="${VERL_MUSA_PATCH}:${PYTHONPATH:-}"

export INFER_BACKEND=sglang
export DATA_DIR=/ipfs/models/gsm8k
export MODEL_DIR=/ipfs/models/Qwen/Qwen3-0.6B

exec bash "scripts/baseline_grpo_gsm8k.sh" \
"+ray_kwargs.ray_init.runtime_env.env_vars.VERL_PLATFORM='musa'" \
"+ray_kwargs.ray_init.runtime_env.env_vars.VERL_USE_EXTERNAL_MODULES='${VERL_USE_EXTERNAL_MODULES}'" \
"+ray_kwargs.ray_init.runtime_env.env_vars.VERL_MUSA_PATCH='${VERL_MUSA_PATCH}'" \
"+ray_kwargs.ray_init.runtime_env.env_vars.PYTHONPATH='${VERL_MUSA_PATCH}:${PYTHONPATH:-}'" \
"+ray_kwargs.ray_init.runtime_env.env_vars.RAY_EXPERIMENTAL_NOSET_MUSA_VISIBLE_DEVICES='${RAY_EXPERIMENTAL_NOSET_MUSA_VISIBLE_DEVICES}'" \
"+ray_kwargs.ray_init.runtime_env.env_vars.RAY_ACCEL_ENV_VAR_OVERRIDE_ON_ZERO='${RAY_ACCEL_ENV_VAR_OVERRIDE_ON_ZERO}'" \
"+ray_kwargs.ray_init.runtime_env.env_vars.MUSA_VISIBLE_DEVICES='${MUSA_VISIBLE_DEVICES}'" \
"+ray_kwargs.ray_init.runtime_env.env_vars.MCCL_LIB='${MCCL_LIB}'" \
"+ray_kwargs.ray_init.runtime_env.env_vars.LD_LIBRARY_PATH='${LD_LIBRARY_PATH}'" \
"+ray_kwargs.ray_init.runtime_env.env_vars.VLLM_PATCH_MUSA_CUSTOM_OPS='${VLLM_PATCH_MUSA_CUSTOM_OPS}'" \
"+ray_kwargs.ray_init.runtime_env.env_vars.SGLANG_MUSA_GRAPH_COMPAT='${SGLANG_MUSA_GRAPH_COMPAT}'" \
trainer.device=musa \
+actor_rollout_ref.rollout.engine_kwargs.sglang.device=musa \
+actor_rollout_ref.rollout.engine_kwargs.sglang.attention_backend=fa3 \
+actor_rollout_ref.rollout.engine_kwargs.sglang.disable_piecewise_cuda_graph=True \
"$@"

```

The script passes the platform settings, `verl-musa-patch`, SGLang options, and
device environment to Ray workers through `runtime_env`. Shell exports alone
are not sufficient for Ray workers.



## 3. Compare Results

Compare `critic/rewards/mean` with the [NVIDIA reference run](https://swanlab.cn/@heavyrain/verl_grpo_gsm8k_math/runs/8h196r8o/chart).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Is there a result on MUSA?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I put this MUSA SwanLab link in this PR summary, at the very top, and the link is: https://swanlab.cn/@wukecong/verl_grpo_gsm8k_math/runs/z4ybajqy/chart


The baseline should:

1. Complete all epochs without a crash or hang.
2. Show an upward reward trend within the first 20 steps.
3. Avoid a flat or collapsing reward curve during the first 100 steps.

## 4. Quick Verification

```bash
python3 -c 'import torch; print(torch.musa.is_available(), torch.musa.device_count())'
```

The output should show that MUSA is available and report the visible device
count. The logs should also contain `[VERL_MUSA_SITE]` bootstrap messages.


## Multi-Node Setup

Start Ray on the head node and workers, then set `NNODES` and run the baseline:

```bash
# Head node
ray start --head --port=6379
export RAY_ADDRESS='auto'

# Worker nodes
ray start --address='<head-ip>:6379'

NNODES=2 bash scripts/baseline_grpo_gsm8k.sh
```

MUSA uses Ray's built-in `GPU` resource. Do not configure a custom `musa`
resource.

## Next Steps

- See [Installation Guide](./install_guidance.md) for image and dependency setup.
- See [development.md — Acceptance Baseline](../development.md#acceptance-baseline-for-new-hardware-adaptation) for the PR checklist.
57 changes: 57 additions & 0 deletions tests/test_plugin_registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@ def test_iluvatar_registered(self):
cls = PlatformRegistry.get("iluvatar")
assert cls is PlatformIluvatar

def test_musa_registered(self):
from verl.plugin.platform.platform_manager import PlatformRegistry
from verl_hardware_plugin.platforms.platform_musa import PlatformMUSA # noqa: F401

assert "musa" in PlatformRegistry.registered_names()
cls = PlatformRegistry.get("musa")
assert cls is PlatformMUSA

def test_xpu_detection_with_env(self):
from verl.plugin.platform.platform_manager import _detect_platform_name
from verl_hardware_plugin.platforms.platform_xpu import PlatformXPU # noqa: F401
Expand Down Expand Up @@ -140,6 +148,22 @@ def test_iluvatar_detection_with_env(self):
with mock.patch.dict(os.environ, {"VERL_PLATFORM": "iluvatar"}):
assert _detect_platform_name() == "iluvatar"

def test_musa_detection_with_env(self):
from verl.plugin.platform.platform_manager import _detect_platform_name
from verl_hardware_plugin.platforms.platform_musa import PlatformMUSA # noqa: F401

with _fresh_registries():
with mock.patch.dict(os.environ, {"VERL_PLATFORM": "musa"}):
assert _detect_platform_name() == "musa"

def test_musa_device_and_vendor_names(self):
from verl_hardware_plugin.platforms.platform_musa import PlatformMUSA

platform = PlatformMUSA()
assert platform.device_name == "musa"
assert platform.vendor_name == "moore_threads"
assert platform.communication_backend_name() == "mccl"


class TestEngineRegistration:
"""Verify that engine classes register correctly."""
Expand Down Expand Up @@ -232,6 +256,39 @@ def test_megatron_iluvatar_engine_registered(self):
is MegatronIluvatarEngineWithLMHead
)

def test_megatron_musa_engine_registered(self):
from verl.workers.engine.base import EngineRegistry
from verl_hardware_plugin.engines.megatron_musa import (
MegatronMUSAEngineWithLMHead,
MegatronMUSAEngineWithValueHead,
)

assert (
EngineRegistry._engines["language_model"]["megatron"][("musa", "moore_threads")]
is MegatronMUSAEngineWithLMHead
)
assert (
EngineRegistry._engines["value_model"]["megatron"][("musa", "moore_threads")]
is MegatronMUSAEngineWithValueHead
)

def test_fsdp_musa_engines_registered(self):
from verl.workers.engine.base import EngineRegistry
from verl_hardware_plugin.engines.fsdp_musa import (
FSDPMUSAEngineWithLMHead,
FSDPMUSAEngineWithValueHead,
)

for backend in ("fsdp", "fsdp2"):
assert (
EngineRegistry._engines["language_model"][backend][("musa", "moore_threads")]
is FSDPMUSAEngineWithLMHead
)
assert (
EngineRegistry._engines["value_model"][backend][("musa", "moore_threads")]
is FSDPMUSAEngineWithValueHead
)

def test_fsdp_enflame_engines_registered(self):
from verl.workers.engine.base import EngineRegistry
from verl_hardware_plugin.engines.fsdp_enflame import (
Expand Down
15 changes: 15 additions & 0 deletions verl_hardware_plugin/engines/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,18 @@ def register_all_engines():
logger.info("Registered engines: megatron_iluvatar")
except Exception as e:
logger.debug("Iluvatar Megatron engines not registered: %s", e)

# Moore Threads MUSA engines (MCCL communication).
try:
from verl_hardware_plugin.engines import fsdp_musa # noqa: F401

logger.info("Registered engines: fsdp_musa")
except Exception as e:
logger.debug("MUSA FSDP engines not registered: %s", e)

try:
from verl_hardware_plugin.engines import megatron_musa # noqa: F401

logger.info("Registered engines: megatron_musa")
except Exception as e:
logger.debug("MUSA Megatron engine not registered: %s", e)
Loading
Loading