Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/pr-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ jobs:
strategy:
fail-fast: false
matrix:
info: [{"num_gpus": 8, "test_file": "test_quick_start_glm4_9B.py"}, {"num_gpus": 8, "test_file": "test_glm4.7_30B_A3B_pd_mooncake.py"}, {"num_gpus": 8, "test_file": "test_qwen3_30B_A3B.py", "use_deepep": "1", "use_fp8_rollout": "1"}, {"num_gpus": 8, "test_file": "test_qwen3.6_35B_A3B_pd_mooncake.py", "use_deepep": "1"}, {"enable_eval": "0", "num_gpus": 8, "test_file": "test_qwen3_30B_A3B_r3.py", "use_deepep": "1", "use_fp8_rollout": "1"}, {"enable_eval": "0", "num_gpus": 8, "test_file": "test_qwen3_30B_A3B_r3.py"}, {"num_gpus": 8, "test_file": "test_qwen3_4B_ppo.py"}, {"num_gpus": 8, "test_file": "test_qwen3_4B_ppo_disaggregate.py"}, {"num_gpus": 8, "test_file": "test_qwen3_4B_ppo_train_critic_only.py"}, {"num_gpus": 8, "test_file": "test_moonlight_16B_A3B.py"}, {"enable_eval": "0", "num_gpus": 8, "test_file": "test_moonlight_16B_A3B_r3.py"}, {"num_gpus": 8, "test_file": "test_mimo_7B_mtp_only_grad.py"}, {"num_gpus": 8, "test_file": "test_qwen2.5_0.5B_debug_rollout_then_train.py"}, {"num_gpus": 8, "test_file": "test_qwen2.5_0.5B_opd_sglang.py"}]
info: [{"num_gpus": 8, "test_file": "test_quick_start_glm4_9B.py"}, {"num_gpus": 8, "test_file": "test_glm4.7_30B_A3B_pd_mooncake.py"}, {"num_gpus": 8, "test_file": "test_qwen3_30B_A3B.py", "use_deepep": "1", "use_fp8_rollout": "1"}, {"num_gpus": 8, "test_file": "test_qwen3.6_35B_A3B_pd_mooncake.py", "use_deepep": "1"}, {"enable_eval": "0", "num_gpus": 8, "test_file": "test_qwen3_30B_A3B_r3.py", "use_deepep": "1", "use_fp8_rollout": "1"}, {"enable_eval": "0", "num_gpus": 8, "test_file": "test_qwen3_30B_A3B_r3.py"}, {"num_gpus": 8, "test_file": "test_qwen3_4B_ppo.py"}, {"num_gpus": 8, "test_file": "test_qwen3_4B_ppo_disaggregate.py"}, {"num_gpus": 8, "test_file": "test_qwen3_4B_ppo_train_critic_only.py"}, {"num_gpus": 8, "test_file": "test_moonlight_16B_A3B.py"}, {"enable_eval": "0", "num_gpus": 8, "test_file": "test_moonlight_16B_A3B_r3.py"}, {"num_gpus": 8, "test_file": "test_mimo_7B_mtp_only_grad.py"}, {"num_gpus": 8, "test_file": "test_qwen2.5_0.5B_debug_rollout_then_train.py"}, {"num_gpus": 8, "test_file": "test_qwen2.5_0.5B_opd_sglang.py"}, {"num_gpus": 4, "test_file": "test_qwen2.5_0.5B_fully_async_short.py"}]
defaults:
run:
working-directory: ${{ github.workspace }}
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/pr-test.yml.j2
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
{'test_file': 'test_mimo_7B_mtp_only_grad.py', 'num_gpus': 8},
{'test_file': 'test_qwen2.5_0.5B_debug_rollout_then_train.py', 'num_gpus': 8},
{'test_file': 'test_qwen2.5_0.5B_opd_sglang.py', 'num_gpus': 8},
{'test_file': 'test_qwen2.5_0.5B_fully_async_short.py', 'num_gpus': 4},
],
},
'e2e-test-precision': {
Expand Down
94 changes: 65 additions & 29 deletions examples/fully_async/README.md
Original file line number Diff line number Diff line change
@@ -1,45 +1,81 @@
# Fully Asynchronous Rollout Example
# Fully-Async Rollout Example

This example shows a simple way to make rollout generation **fully asynchronous**: a single global worker is created once and then keeps running in the background, continuously pulling prompts and launching generation tasks. Training only needs to fetch already finished results. This removes the per‑step wait that happens in the normal synchronous style.
End-to-end demo of slime's fully-async rollout path. A background asyncio
worker keeps a fixed pool of in-flight generations across rollout boundaries,
so the next training step doesn't wait for the slowest in-flight sample.
The worker itself lives in `slime.rollout.fully_async_rollout`; this
directory is just the launch script + CI test.

## Files
* `fully_async_rollout.py`: global async worker + `generate_rollout_fully_async` entry.
* `run-qwen3-4b-fully_async.sh`: example launch script with Qwen3‑4B.

## Prerequisite
First set up model & environment following the Qwen3-4B example.
* `run-qwen2.5-0.5B-fully_async.sh` — single-node, 4-GPU, three-rollout demo
with Qwen2.5-0.5B-Instruct on dapo-math-17k. Fast enough to be the CI
smoke test for the fully-async path.

The same script doubles as `tests/test_qwen2.5_0.5B_fully_async_short.py` in
CI.

## Prerequisites

```
/root/models/Qwen2.5-0.5B-Instruct/ # HF checkpoint
/root/models/Qwen2.5-0.5B-Instruct_torch_dist/ # tools/convert_hf_to_torch_dist.py
/root/datasets/dapo-math-17k/dapo-math-17k.jsonl
```

## Run

## Quick Start
```bash
cd slime
bash examples/fully_async/run-qwen3-4b-fully_async.sh
bash examples/fully_async/run-qwen2.5-0.5B-fully_async.sh
```
You should see log lines like:

You should see:

```
Creating new global async worker...
Continuous async rollout worker started
fully-async rollout 0: target=8 queue_warm=0
fully-async rollout 0: done in ...s, queue_left=...
```

## How It Works (Very Short)
* First call: create `AsyncRolloutWorker` (thread + asyncio loop).
* Loop keeps up to `--rollout-batch-size` tasks in flight using `generate_and_rm_group`.
* Completed groups are pushed into a queue; caller drains until it has enough samples.
* Worker is stopped automatically at process exit.

## Limitations
* No evaluation mode.
* Ordering is best effort (sorted at the end by index).
* Minimal error handling.
## How To Plug Your Own Generate Into This

## Config Differences (2 Key Points)
To enable the fully async pattern there are only two changes compared to a normal run:
Two pieces flip the standard pipeline into fully-async:

1. Use the async training driver: `train_async.py` (not `train.py`).
1. Use the async training driver: `python3 train_async.py` (not `train.py`).
2. Set the rollout function path:
```bash
--rollout-function-path fully_async_rollout.generate_rollout_fully_async
```
```
--rollout-function-path slime.rollout.fully_async_rollout.generate_rollout_fully_async
```

For custom per-sample logic, use slime's standard plug-in points — they
work unchanged under fully-async:

```
--custom-generate-function-path your.module.generate # (args, sample, sampling_params) -> Sample | list[Sample]
--custom-rm-path your.module.reward # (args, sample | list[Sample]) -> float | list[float]
```

Why is it still "fully" async although `train_async.py` itself schedules rollouts step‑by‑step?
See `examples/swe_codex/` for a non-trivial example that plugs in a
multi-turn agent (Claude Code in a Docker-Proxy sandbox) this way.

## Worker Internals (Very Short)

* First call: create a process-wide `AsyncRolloutWorker` (thread + asyncio
loop). The worker is shared across all subsequent `generate_rollout`
calls so its queue stays warm.
* Loop keeps up to `args.sglang_server_concurrency` tasks in flight using
`generate_and_rm_group`.
* Completed groups land on an output queue; each `generate_rollout` call
drains until it has `rollout_batch_size` groups and returns them sorted
by `sample.index`.
* Groups containing an `ABORTED` sample are pushed back into
`data_buffer.add_samples` instead of being shipped to training.
* Worker is stopped automatically at process exit via `atexit`.

## Limitations

Because the real generation work is done by a **persistent background worker** created in `generate_rollout_fully_async`. Each call from `train_async.py` only drains already completed samples from the worker's output queue; the worker has been continuously generating since the first call. Thus rollout production (model inference) and training consume happen in parallel with minimal waiting.
* No evaluation mode (would conflict with the continuous-running model).
* Ordering across rollouts is best-effort — within a rollout, groups are
sorted by index before being handed to training.
* TODO: partial-rollout-style resume for `ABORTED` trajectories is not
yet wired; for now the trajectory is re-queued and starts over.
263 changes: 0 additions & 263 deletions examples/fully_async/fully_async_rollout.py

This file was deleted.

Loading
Loading