Skip to content
Open
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
85 changes: 40 additions & 45 deletions scripts/vllm_dissag/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,31 @@ How the pieces fit, from sbatch down to the per-node `vllm serve` workers.

```mermaid
flowchart TB
user([user / CI]) -->|"sbatch + env\n(CONNECTOR, WIDE_EP, EP_BACKEND,\nMODEL_NAME, xP, yD, RUN_MORI/RUN_DEEPEP)"| slurm
user([user / CI]) -->|"sbatch + env<br/>(CONNECTOR, WIDE_EP, EP_BACKEND,<br/>MODEL_NAME, xP, yD, RUN_MORI/RUN_DEEPEP)"| slurm

subgraph host["Submit host"]
slurm["run_xPyD_models.slurm\n• resolve script dir\n• validate MODEL_NAME (VALID_MODELS)\n• axis resolution + back-compat shim\n• pick nodes, gather IPs\n• docker run per node (-e env plumb)"]
slurm["run_xPyD_models.slurm<br/>• resolve script dir<br/>• validate MODEL_NAME (VALID_MODELS)<br/>• axis resolution + back-compat shim<br/>• pick nodes, gather IPs<br/>• docker run per node (-e env plumb)"]
end

slurm -->|"srun + docker run\n(one container per node)"| n0
slurm -->|"srun + docker run<br/>(one container per node)"| n0
slurm --> n1
slurm --> n2
slurm --> n3

subgraph cluster["Allocated nodes (xP prefill + yD decode)"]
n0["NODE_RANK 0\nPrefill MASTER + Proxy"]
n1["NODE_RANK 1..xP-1\nPrefill CHILD"]
n2["NODE_RANK xP\nDecode MASTER"]
n3["NODE_RANK xP+1..end\nDecode CHILD"]
n0["NODE_RANK 0<br/>Prefill MASTER + Proxy"]
n1["NODE_RANK 1..xP-1<br/>Prefill CHILD"]
n2["NODE_RANK xP<br/>Decode MASTER"]
n3["NODE_RANK xP+1..end<br/>Decode CHILD"]
end

subgraph driverbox["Inside each container: vllm_disagg.sh (the one launcher)"]
driver["DRIVER\n• axis select + validate\n• topology math\n• models.yaml parse\n• role branch\n• barrier / benchmark / cleanup"]
para["parallelism.sh\nTP vs wideEP arg helpers"]
yaml[("models.yaml\nper-model flags + env")]
driver["DRIVER<br/>• axis select + validate<br/>• topology math<br/>• models.yaml parse<br/>• role branch<br/>• barrier / benchmark / cleanup"]
para["parallelism.sh<br/>TP vs wideEP arg helpers"]
yaml[("models.yaml<br/>per-model flags + env")]
conn{{"connectors/<CONNECTOR>.sh"}}
rixl["rixl.sh\nNixlConnector\nTP + DeepEP"]
moriio["moriio.sh\nMoRIIOConnector\nMoRIIO+TP + MoRI-EP"]
rixl["rixl.sh<br/>NixlConnector<br/>TP + DeepEP"]
moriio["moriio.sh<br/>MoRIIOConnector<br/>MoRIIO+TP + MoRI-EP"]
driver --> para
driver --> yaml
driver --> conn
Expand All @@ -45,9 +45,9 @@ flowchart TB
end

n0 --> driverbox
driver -->|connector_launch_worker| vllm[["vllm serve\n(prefill / decode worker)"]]
driver -->|rank 0 only| proxy[["proxy / router\n(co-located)"]]
proxy --> bench["benchmark_xPyD.sh\n→ *_CONCURRENCY.log"]
driver -->|connector_launch_worker| vllm[["vllm serve<br/>(prefill / decode worker)"]]
driver -->|rank 0 only| proxy[["proxy / router<br/>(co-located)"]]
proxy --> bench["benchmark_xPyD.sh<br/>→ *_CONCURRENCY.log"]
```

---
Expand All @@ -62,24 +62,24 @@ flowchart TD
start([env in]) --> q0{CONNECTOR set?}

q0 -->|no| shim{legacy flag?}
shim -->|RUN_MORI=1| m["CONNECTOR=moriio\nWIDE_EP=1\nEP_BACKEND=mori"]
shim -->|RUN_DEEPEP=1| d["CONNECTOR=rixl\nWIDE_EP=1\nEP_BACKEND=deepep"]
shim -->|neither| def["CONNECTOR=rixl\nWIDE_EP=0 (TP)"]
q0 -->|yes| explicit["use explicit\nCONNECTOR / WIDE_EP / EP_BACKEND"]
shim -->|RUN_MORI=1| m["CONNECTOR=moriio<br/>WIDE_EP=1<br/>EP_BACKEND=mori"]
shim -->|RUN_DEEPEP=1| d["CONNECTOR=rixl<br/>WIDE_EP=1<br/>EP_BACKEND=deepep"]
shim -->|neither| def["CONNECTOR=rixl<br/>WIDE_EP=0 (TP)"]
q0 -->|yes| explicit["use explicit<br/>CONNECTOR / WIDE_EP / EP_BACKEND"]

m --> vconn
d --> vconn
def --> vconn
explicit --> vconn

vconn{validate CONNECTOR\nin rixl|moriio}
vconn{"validate CONNECTOR<br/>in rixl|moriio"}
vconn -->|invalid| err1[["abort: invalid CONNECTOR"]]
vconn -->|valid| vwide{validate WIDE_EP\nin 0|1}
vconn -->|valid| vwide{"validate WIDE_EP<br/>in 0|1"}
vwide -->|invalid| err2[["abort: invalid WIDE_EP"]]
vwide -->|valid| qwide{WIDE_EP == 1?}

qwide -->|no (TP)| okTP["EP_BACKEND = n/a"]
qwide -->|yes wideEP| qpair{connector ↔ EP_BACKEND}
qwide -->|"no (TP)"| okTP["EP_BACKEND = n/a"]
qwide -->|yes wideEP| qpair{"connector ↔ EP_BACKEND"}
qpair -->|moriio + mori| okM["OK: all2all = mori_*"]
qpair -->|rixl + deepep| okD["OK: all2all = deepep_*"]
qpair -->|moriio + deepep| err3[["abort: cross-pair"]]
Expand All @@ -95,12 +95,12 @@ flowchart TD
```mermaid
flowchart LR
subgraph TP["WIDE_EP=0 (TP, PARALLEL_MODE=tp)"]
rt["rixl + TP\nNixlConnector\n--tensor-parallel-size"]
mt["moriio + TP (NEW)\nMoRIIOConnector\n--tensor-parallel-size"]
rt["rixl + TP<br/>NixlConnector<br/>--tensor-parallel-size"]
mt["moriio + TP (NEW)<br/>MoRIIOConnector<br/>--tensor-parallel-size"]
end
subgraph EP["WIDE_EP=1 (wideEP, PARALLEL_MODE=dp)"]
rd["rixl + deepep\nNixlConnector\n-tp 1 --data-parallel-size\n--enable-expert-parallel\n--all2all-backend deepep_*"]
md["moriio + mori\nMoRIIOConnector\n-tp 1 --data-parallel-size\n--enable-expert-parallel\n--all2all-backend mori_*"]
rd["rixl + deepep<br/>NixlConnector<br/>-tp 1 --data-parallel-size<br/>--enable-expert-parallel<br/>--all2all-backend deepep_*"]
md["moriio + mori<br/>MoRIIOConnector<br/>-tp 1 --data-parallel-size<br/>--enable-expert-parallel<br/>--all2all-backend mori_*"]
end
```

Expand All @@ -116,16 +116,16 @@ stateDiagram-v2
[*] --> ResolveAxes
ResolveAxes --> ParseModel: CONNECTOR/WIDE_EP/EP_BACKEND valid
ResolveAxes --> Abort: invalid / cross-pair
ParseModel: Parse models.yaml\n(export env: block,\nresolve prefill/decode flags)
ParseModel: Parse models.yaml<br/>(export env: block,<br/>resolve prefill/decode flags)
ParseModel --> LoadProfiles
LoadProfiles: source parallelism.sh +\nconnectors/<CONNECTOR>.sh →\nconnector_init
LoadProfiles: source parallelism.sh +<br/>connectors/<CONNECTOR>.sh →<br/>connector_init

LoadProfiles --> DryRunEmit: DRY_RUN=1
DryRunEmit: emit assembled\nvllm serve argv
DryRunEmit: emit assembled<br/>vllm serve argv
DryRunEmit --> [*]

LoadProfiles --> Barrier: normal run
Barrier: container barrier\n(socket_barrier.py)\n+ connector_runtime_patch
Barrier: container barrier<br/>(socket_barrier.py)<br/>+ connector_runtime_patch
Barrier --> RoleBranch

state RoleBranch <<choice>>
Expand All @@ -134,16 +134,16 @@ stateDiagram-v2
RoleBranch --> DecodeMaster: RANK==xP
RoleBranch --> DecodeChild: RANK>xP

PrefillMaster: launch worker (kv_producer)\n+ wait_workers_ready\n+ start_proxy + benchmark
PrefillMaster: launch worker (kv_producer)<br/>+ wait_workers_ready<br/>+ start_proxy + benchmark
PrefillChild: launch worker (kv_producer, headless)
DecodeMaster: launch worker (kv_consumer)
DecodeChild: launch worker (kv_consumer, headless)

PrefillMaster --> Cleanup: bench done →\nkill proxy + worker
PrefillMaster --> Cleanup: bench done →<br/>kill proxy + worker
PrefillChild --> WaitProxy
DecodeMaster --> WaitProxy
DecodeChild --> WaitProxy
WaitProxy: wait_for_proxy_and_cleanup\n(barrier → wait close → kill)
WaitProxy: wait_for_proxy_and_cleanup<br/>(barrier → wait close → kill)
WaitProxy --> Cleanup
Cleanup --> [*]
Abort --> [*]
Expand Down Expand Up @@ -189,12 +189,12 @@ sequenceDiagram

| Hook | rixl.sh | moriio.sh |
|------|---------|-----------|
| `connector_init` | ports 2584/router; PROXY_TYPE; barrier 5000/15000 | ports 20005/10001; per-role MoRI backend; barrier 2222 |
| `connector_init` | serve 2584; `ROUTER_PORT` 18001; barrier 5000 (TP) / 15000 (deepep); `PROXY_TYPE` vllm_router\|toy_proxy | serve 20005; `ROUTER_PORT` 30000, toy 10001; barrier 2222; `PROXY_TYPE` vllm_router\|moriio_toy |
| `connector_setup_env` | UCX/NIXL (TP) or RocSHMEM/UCX/NIXL+#39276 sed (deepep) | MoRI/RDMA/RocSHMEM + caches |
| `connector_runtime_patch` | no-op (deepep patches inline in setup_env) | no-op (disagg fixes are in-source in the image's vLLM) |
| `connector_launch_worker` | TP server **or** deepep DP+EP server | MoRIIO+TP server **or** MoRI-EP DP+EP server |
| `connector_wait_workers_ready` | TP: socket_barrier; deepep: log-signal | log-signal on prefill+decode masters |
| `connector_start_proxy` | vllm_router / toy_proxy over all P/D IPs | moriio_toy_proxy (or router) + curl probe |
| `connector_start_proxy` | vllm_router / toy_proxy over all P/D IPs | vllm_router / moriio_toy + curl probe |

---

Expand All @@ -206,19 +206,19 @@ Where each piece of configuration lives, and the precedence when they overlap.
flowchart TD
subgraph yamlcat["models.yaml (per model)"]
bf["base_flags"]
mf["tp_flags / dp_flags\n(by WIDE_EP)"]
mf["tp_flags / dp_flags<br/>(by WIDE_EP)"]
rf["prefill.{tp,dp} / decode.{tp,dp}"]
ef["experimental_flags"]
envb["env: { VAR: val }"]
end
bf & mf & rf & ef --> compose["MODEL_CONFIG_PREFILL / _DECODE\n(passed to connector_launch_worker)"]
bf & mf & rf & ef --> compose["MODEL_CONFIG_PREFILL / _DECODE<br/>(passed to connector_launch_worker)"]

subgraph envlayer["ENV precedence (low → high)"]
c1["connector default\nexport VAR=\${VAR:-default}"] --> c2["models.yaml env:\n(exported before setup_env)"] --> c3["slurm -e VAR=…\n(submit/site override, wins)"]
c1["connector default<br/>export VAR=\${VAR:-default}"] --> c2["models.yaml env:<br/>(exported before setup_env)"] --> c3["slurm -e VAR=…<br/>(submit/site override, wins)"]
end
envb -.-> c2

note["Launcher owns: connector, transfer,\nparallelism DEGREE (--tensor/data-parallel-size,\n--all2all-backend, kv-transfer-config).\nyaml owns: model-tuning flags + env only."]
note["Launcher owns: connector, transfer,<br/>parallelism DEGREE (--tensor/data-parallel-size,<br/>--all2all-backend, kv-transfer-config).<br/>yaml owns: model-tuning flags + env only."]
```

---
Expand All @@ -234,10 +234,5 @@ flowchart TD
| `connectors/moriio.sh` | MoRIIOConnector profile (MoRIIO+TP + MoRI-EP) |
| `connectors/{rixl,moriio}.env` | per-connector platform env (expandable_segments:False etc.), forwarded via `docker -e` |
| `models.yaml` | per-model flags + env catalog |
| `tests/gate_check.sh` | combo-gate unit tests (model × connector × WIDE_EP allow/reject) |
| `tests/argv_assert.sh` | per-cell `vllm serve` flag/env assertions from the driver's `DRY_RUN=1` output |
| `tests/run_all.sh` | runs all offline gates (gate_check + argv_assert) |
| `tests/{drive_cell,harvest,run_interactive}.sh` | interactive-allocation live-test drivers |
| `tests/TEST_PLAN.md` | before/after verification plan |
| `benchmark_xPyD.sh`, `benchmark_long_context.sh`, `benchmark_niah.{sh,py}`, `benchmark_parser.py`, `parse_to_csv.py` | benchmark + parsing (NIAH = long-context retrieval, vllm#47042) |
| `socket_barrier.py`, `socket_wait.py`, `salloc_launch.sh` | node coordination + salloc helper |
73 changes: 38 additions & 35 deletions scripts/vllm_dissag/README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,16 @@ slurm gate rejects any other pairing. Dense models are TP-only; the DeepSeek fam

- A Slurm cluster with `xP + yD` nodes (minimum 2: `xP=1`, `yD=1`), 8 GPUs/node.
- A Docker image with vLLM + the KV connector(s) + NIC drivers. See **Building the Docker image**.
- Model weights present on all allocated nodes under `/mnt/m2m_nobackup/models_blog/` or
`/shared_inference/models_blog/` (or set `MODEL_DIR`).
- Model weights present on all allocated nodes at `$MODEL_DIR/$MODEL_NAME` (`MODEL_DIR` default
`/shared_inference/models_blog/`). The slurm resolves `MODEL_PATH` from these; override
`MODEL_DIR` to point elsewhere.
- A shared filesystem for logs.

## Building the Docker image

No public prebuilt image ships — build your own from the one fullsource Dockerfile (MoRI +
AITER + vLLM + vllm-router + optional UCX/RIXL/rocSHMEM/DeepEP, from Git pins on an open
ROCm ci_base):
No public prebuilt image ships — build your own from the single Dockerfile
`docker/vllm_disagg_inference.ubuntu.amd.Dockerfile` (MoRI + AITER + vLLM + vllm-router +
optional UCX/RIXL/rocSHMEM/DeepEP, from Git pins on an open ROCm ci_base):

```bash
cd MAD
Expand Down Expand Up @@ -172,10 +173,12 @@ separate port from the vLLM server.

**Port defaults by connector:**

| Connector | vLLM server port | Proxy port |
|-----------|------------------|------------|
| `rixl` (TP or DeepEP) | 2584 | `ROUTER_PORT` (30000 default) |
| `moriio` (MoRI-EP or TP) | 20005 | `ROUTER_PORT` (30000) with the default vllm_router; 10001 with `PROXY_TYPE=moriio_toy` |
| Connector | vLLM server port | Proxy port (`vllm_router`) | Toy-proxy port |
|-----------|------------------|----------------------------|----------------|
| `rixl` (TP or DeepEP) | 2584 | `ROUTER_PORT` (default 18001) | 18001 (`toy_proxy`) |
| `moriio` (MoRI-EP or TP) | 20005 | `ROUTER_PORT` (default 30000) | 10001 (`moriio_toy`) |

Override any port with the matching env var (`ROUTER_PORT`, `MORI_SERVE_PORT`, etc.).

## Proxy options

Expand All @@ -184,24 +187,22 @@ separate port from the vLLM server.

`ROUTER_BINARY` (both connectors): path to the `vllm-router` binary. Images that bake the
router ship it on `PATH`, so this can be left unset. Runtime-only images that do NOT ship a
router (e.g. the mori121 MoRI-EP image) require `ROUTER_BINARY=<path>` — point it at a built
router require `ROUTER_BINARY=<path>` — point it at a built
`vllm-router` on a shared mount. If unset and none is on `PATH`, the launcher aborts with a
clear error.

## Validation status (image capability)

The consolidated launcher assembles correct argv for all 4 combos (offline gates in `tests/`),
but each combo also needs the connector's transport built into the image:
## Image capability by combo

| Combo | Live-validated | Image requirement |
|-------|----------------|-------------------|
| moriio + TP | ✅ | MoRI (mori121 image) |
| moriio + wideEP (mori) | ✅ 1P/1D + 2P/2D | MoRI (mori121 image) |
| rixl + TP | needs a NIXL-validated image | NixlConnector + working NCCL TP path |
| rixl + wideEP (deepep) | needs a DeepEP-validated image | DeepEP all2all kernels |
The launcher assembles correct argv for all 4 combos, but each combo needs its transport
built into the image. Build with `WITH_NIXL=1` (default) to get all four; `WITH_NIXL=0` gives
a MoRI-only image (moriio TP/wideEP).

The `mori_ep_fullsource` Dockerfile builds the MoRI stack; rixl/DeepEP combos need an image
where those transports are validated (e.g. develop's `vllm_disagg_inference` for NIXL).
| Combo | Transport needed in image |
|-------|---------------------------|
| moriio + TP | MoRI |
| moriio + wideEP (mori) | MoRI |
| rixl + TP | NixlConnector (+ working NCCL TP path) |
| rixl + wideEP (deepep) | DeepEP all2all kernels |

## DeepEP options (optional)

Expand All @@ -226,6 +227,18 @@ Parse a results log:
python3 benchmark_parser.py <log_path>/benchmark_XXX_CONCURRENCY.log
```

The default sweep runs `benchmark_xPyD.sh`. Two alternatives plug in via `BENCHMARK_SCRIPT_FILE`:

- **`benchmark_long_context.sh`** — steady-state long-context throughput/latency with per-shape
warmup (each ISL/OSL cell is warmed before measurement, so JIT/cudagraph/handshake costs are
excluded). Use for large-ISL runs, e.g. `BENCHMARK_COMBINATIONS="28000/1000"`.
- **`benchmark_niah.sh`** — long-context *retrieval* accuracy (see below).

```bash
export BENCHMARK_SCRIPT_FILE=benchmark_long_context.sh
export BENCHMARK_COMBINATIONS="28000/1000" BENCHMARK_CON="1 4 8"
```

### Long-context retrieval (NIAH)

`benchmark_niah.{sh,py}` run a needle-in-a-haystack retrieval sweep (adapted from
Expand All @@ -241,20 +254,10 @@ export NIAH_WORDS="2000,8000,20000,35000" # context sizes (words); optional
Or run `benchmark_niah.py` standalone against any live OpenAI-compatible endpoint
(`NIAH_URL`, `NIAH_MODEL`).

## Testing the launcher (no GPUs)

Offline suites validate the launcher without a cluster — run after any change to the
launcher/connectors/yaml:

```bash
bash tests/run_all.sh # gate_check + argv_assert; expect ALL OFFLINE SUITES PASSED
```

- `tests/gate_check.sh` — connector × WIDE_EP combo validation (valid/invalid pairings, back-compat shims).
- `tests/argv_assert.sh` — asserts expected `vllm serve` flags/env are present/absent per cell, from
the launcher's `DRY_RUN=1` output (no GPUs, no container).
## Inspecting the launcher (no GPUs)

You can also dry-run any single cell to inspect the assembled command:
Dry-run any cell to print the assembled `vllm serve` argv without launching — useful to
sanity-check flags after editing the launcher/connectors/`models.yaml`:

```bash
CONNECTOR=moriio WIDE_EP=1 EP_BACKEND=mori DRY_RUN=1 NODE_RANK=0 \
Expand Down
4 changes: 2 additions & 2 deletions scripts/vllm_dissag/connectors/moriio.env
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Platform-mandatory runtime env for the moriio (MoRIIO) connector on the
# ROCm-7.2.3 / mori121 image. Applies to ALL moriio runs (TP and wideEP, every model).
# Platform-mandatory runtime env for the moriio (MoRIIO) connector on ROCm 7.2.3.
# Applies to ALL moriio runs (TP and wideEP, every model).
#
# These must reach the container at PID 1 — PyTorch reads the alloc-conf at import,
# so a late shell export inside the launcher is too late. The slurm sources this file
Expand Down
Loading