Unverified Commit c6e3db5e authored by ishandhanani's avatar ishandhanani Committed by GitHub
Browse files

chore: update sglang to 0.5.3.post1 to take advantage of container + doc cleanup (#3621)

parent f26d8da7
......@@ -189,7 +189,7 @@ class BaseWorkerHandler(ABC):
Yields:
asyncio.Task: The cancellation monitoring task being managed
"""
logging.info(f"Creating cancellation monitor task for Context: {context.id()}")
logging.debug(f"Creating cancellation monitor task for Context: {context.id()}")
# Start the cancellation monitoring task
cancellation_task = asyncio.create_task(
......
......@@ -14,7 +14,7 @@ ARG RUNTIME_IMAGE="nvcr.io/nvidia/cuda"
ARG RUNTIME_IMAGE_TAG="12.8.1-runtime-ubuntu24.04"
# Make sure to update the dependency version in pyproject.toml when updating this
ARG SGLANG_VERSION="0.5.3"
ARG SGLANG_VERSION="0.5.3.post1"
# Define general architecture ARGs for supporting both x86 and aarch64 builds.
......
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
ARG SGLANG_IMAGE_TAG="v0.5.3-cu126"
ARG SGLANG_IMAGE_TAG="v0.5.3.post1"
ARG BRANCH_TYPE
FROM lmsysorg/sglang:${SGLANG_IMAGE_TAG}
ARG MODE="hopper"
ARG ARCH="amd64"
ARG ARCH_ALT="x86_64"
ARG NIXL_UCX_REF="v1.19.0"
ARG NIXL_TAG="0.5.0"
ARG CMAKE_VERSION="3.31.8"
ARG RUST_VERSION="1.90.0"
ARG CARGO_BUILD_JOBS="16"
RUN apt-get update -y && \
apt-get install -y \
cmake meson ninja-build pybind11-dev patchelf net-tools \
build-essential protobuf-compiler libssl-dev pkg-config git \
clang libclang-dev git rapidjson-dev zlib1g-dev jq && \
pip install --break-system-packages meson-python wheel build
# Build UCX + NIXL for x86/hopper until its fully tested on GB200
RUN if [ "$MODE" = "hopper" ]; then \
apt-get install -y --no-install-recommends \
libibverbs-dev rdma-core ibverbs-utils libibumad-dev \
libnuma-dev librdmacm-dev ibverbs-providers autoconf libtool && \
# UCX from source
rm -rf /opt/hpcx/ucx /usr/local/ucx && \
cd /usr/local/src && \
git clone https://github.com/openucx/ucx.git && \
cd ucx && git checkout $NIXL_UCX_REF && \
./autogen.sh && \
./configure \
--prefix=/usr/local/ucx \
--enable-shared \
--disable-static \
--disable-doxygen-doc \
--enable-optimizations \
--enable-cma \
--enable-devel-headers \
--with-cuda=/usr/local/cuda \
--with-verbs \
--with-efa \
--with-dm \
--with-gdrcopy=/usr/local \
--enable-mt && \
make -j && make install-strip && ldconfig && \
# NIXL
git clone https://github.com/ai-dynamo/nixl.git /opt/nixl && \
cd /opt/nixl && git checkout $NIXL_TAG && \
pip install --break-system-packages . \
--config-settings="setup-args=-Ducx_path=/usr/local/ucx"; \
fi
FROM scratch AS local_src
COPY . /src
ENV LD_LIBRARY_PATH=/usr/lib:/usr/local/ucx/lib:$LD_LIBRARY_PATH
FROM lmsysorg/sglang:${SGLANG_IMAGE_TAG}
# Dynamo
WORKDIR /sgl-workspace
COPY . /sgl-workspace/dynamo
ENV RUSTUP_HOME=/usr/local/rustup \
CARGO_HOME=/usr/local/cargo \
PATH=/usr/local/cargo/bin:$PATH
RUN wget --tries=3 --waitretry=5 \
"https://static.rust-lang.org/rustup/archive/1.28.1/${ARCH_ALT}-unknown-linux-gnu/rustup-init" && \
chmod +x rustup-init && \
./rustup-init -y \
--no-modify-path \
--profile minimal \
--default-toolchain $RUST_VERSION \
--default-host ${ARCH_ALT}-unknown-linux-gnu && \
rm rustup-init && \
chmod -R a+w $RUSTUP_HOME $CARGO_HOME
ARG CARGO_BUILD_JOBS
ENV CARGO_BUILD_JOBS=${CARGO_BUILD_JOBS}
RUN cd dynamo && cargo build --release
# Install dynamo
# Providing --build-arg BRANCH_TYPE=local will editable install the local dynamo repo
# Providing --build-arg BRANCH_TYPE=remote will editable install the remote dynamo repo
# Default is to install the latest published dynamo version
ARG BRANCH_TYPE
COPY --from=local_src /src /tmp/local_src
RUN if [ "$BRANCH_TYPE" = "local" ]; then \
cp -r /tmp/local_src /sgl-workspace/dynamo; \
elif [ "$BRANCH_TYPE" = "remote" ]; then \
git clone https://github.com/ai-dynamo/dynamo.git /sgl-workspace/dynamo; \
fi
RUN cd dynamo/lib/bindings/python && \
# SGLang does not use a venv in their container
RUN if [ "$BRANCH_TYPE" = "local" ]; then \
cd dynamo/lib/bindings/python && \
pip install --break-system-packages maturin && \
maturin build --release && \
pip install --break-system-packages target/wheels/*.whl && \
cd /sgl-workspace/dynamo && \
pip install --break-system-packages -e . && \
pip install --break-system-packages --requirement /tmp/local_src/container/deps/requirements.txt ; \
elif [ "$BRANCH_TYPE" = "remote" ]; then \
cd dynamo/lib/bindings/python && \
pip install --break-system-packages maturin && \
maturin build --release && \
pip install --break-system-packages target/wheels/*.whl && \
cd /sgl-workspace/dynamo && \
pip install --break-system-packages .
RUN pip install --break-system-packages sglang-router==0.1.9
# Install dependencies
RUN --mount=type=bind,source=./container/deps/requirements.txt,target=/tmp/requirements.txt \
pip install --break-system-packages --requirement /tmp/requirements.txt
RUN wget --tries=3 --waitretry=5 \
pip install --break-system-packages -e . && \
pip install --break-system-packages --requirement /sgl-workspace/dynamo/container/deps/requirements.txt ; \
else \
pip install --break-system-packages ai-dynamo ; \
fi \
&& rm -rf /tmp/local_src
# Install NATS and ETCD
RUN case "$(uname -m)" in \
x86_64) ARCH=amd64 ;; \
aarch64) ARCH=arm64 ;; \
*) echo "Unsupported architecture: $(uname -m)" && exit 1 ;; \
esac && \
wget --tries=3 --waitretry=5 \
https://github.com/nats-io/nats-server/releases/download/v2.10.28/\
nats-server-v2.10.28-${ARCH}.deb && \
dpkg -i nats-server-v2.10.28-${ARCH}.deb && rm nats-server-v2.10.28-${ARCH}.deb
ENV ETCD_VERSION="v3.5.21"
RUN wget --tries=3 --waitretry=5 \
RUN case "$(uname -m)" in \
x86_64) ARCH=amd64 ;; \
aarch64) ARCH=arm64 ;; \
*) echo "Unsupported architecture: $(uname -m)" && exit 1 ;; \
esac && \
wget --tries=3 --waitretry=5 \
https://github.com/etcd-io/etcd/releases/download/${ETCD_VERSION}/\
etcd-${ETCD_VERSION}-linux-${ARCH}.tar.gz -O /tmp/etcd.tar.gz && \
mkdir -p /usr/local/bin/etcd && \
......@@ -107,29 +72,6 @@ etcd-${ETCD_VERSION}-linux-${ARCH}.tar.gz -O /tmp/etcd.tar.gz && \
ENV PATH=/usr/local/bin/etcd:$PATH
# GenAI Perf
RUN apt-get purge -y cmake
RUN mkdir /sgl-workspace/cmake_build && \
cd /sgl-workspace/cmake_build && \
wget https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/\
cmake-${CMAKE_VERSION}-linux-$(uname -m).tar.gz && \
tar -xzf cmake-${CMAKE_VERSION}-linux-$(uname -m).tar.gz && \
mv cmake-${CMAKE_VERSION}-linux-$(uname -m) custom_cmake && \
rm cmake-${CMAKE_VERSION}-linux-$(uname -m).tar.gz
ENV PATH=/sgl-workspace/cmake_build/custom_cmake/bin:$PATH
RUN cmake --version
RUN git clone --depth=1 \
https://github.com/triton-inference-server/perf_analyzer.git && \
mkdir perf_analyzer/build && \
cmake -B perf_analyzer/build -S perf_analyzer && \
cmake --build perf_analyzer/build -- -j$(nproc)
ENV PATH=/sgl-workspace/perf_analyzer/build/perf_analyzer/src/perf-analyzer-build:$PATH
RUN pip install --break-system-packages aiperf
# Enable forceful shutdown of inflight requests
ENV SGL_FORCE_SHUTDOWN=1
......
......@@ -5,8 +5,6 @@ SPDX-License-Identifier: Apache-2.0
# Running SGLang with Dynamo
This directory contains an SGLang component for Dynamo and reference implementations for deploying Large Language Models (LLMs) in various configurations using SGLang. SGLang internally uses ZMQ to communicate between the ingress and the engine processes. For Dynamo, we leverage the runtime to communicate directly with the engine processes and handle ingress and pre/post processing on our end.
## Use the Latest Release
We recommend using the latest stable release of dynamo to avoid breaking changes:
......@@ -24,6 +22,7 @@ git checkout $(git describe --tags $(git rev-list --tags --max-count=1))
## Table of Contents
- [Feature Support Matrix](#feature-support-matrix)
- [Dynamo SGLang Integration](#dynamo-sglang-integration)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [Single Node Examples](#run-single-node-examples)
- [Multi-Node and Advanced Examples](#advanced-examples)
......@@ -40,17 +39,8 @@ git checkout $(git describe --tags $(git rev-list --tags --max-count=1))
| [**KV-Aware Routing**](../../architecture/kv_cache_routing.md) | ✅ | |
| [**SLA-Based Planner**](../../architecture/sla_planner.md) | ✅ | |
| [**Multimodal EPD Disaggregation**](multimodal_epd.md) | ✅ | |
| [**Load Based Planner**](../../architecture/load_planner.md) | ❌ | Planned |
| [**KVBM**](../../architecture/kvbm_architecture.md) | ❌ | Planned |
### Large Scale P/D and WideEP Features
| Feature | SGLang | Notes |
|---------------------|--------|--------------------------------------------------------------|
| **WideEP** | ✅ | Full support on H100s/GB200 |
| **DP Rank Routing** | 🚧 | Direct routing supported. Dynamo KV router does not router to DP worker |
| **GB200 Support** | ✅ | |
## Dynamo SGLang Integration
......@@ -65,7 +55,7 @@ Dynamo SGLang uses SGLang's native argument parser, so **most SGLang engine argu
| Argument | Description | Default | SGLang Equivalent |
|----------|-------------|---------|-------------------|
| `--endpoint` | Dynamo endpoint in `dyn://namespace.component.endpoint` format | Auto-generated based on mode | N/A |
| `--migration-limit` | Max times a request can migrate between workers | `0` (disabled) | N/A |
| `--migration-limit` | Max times a request can migrate between workers for fault tolerance. See [Request Migration Architecture](../../../docs/architecture/request_migration.md). | `0` (disabled) | N/A |
| `--dyn-tool-call-parser` | Tool call parser for structured outputs (takes precedence over `--tool-call-parser`) | `None` | `--tool-call-parser` |
| `--dyn-reasoning-parser` | Reasoning parser for CoT models (takes precedence over `--reasoning-parser`) | `None` | `--reasoning-parser` |
| `--use-sglang-tokenizer` | Use SGLang's tokenizer instead of Dynamo's | `False` | N/A |
......@@ -73,39 +63,33 @@ Dynamo SGLang uses SGLang's native argument parser, so **most SGLang engine argu
#### Tokenizer Behavior
- **Default (`--use-sglang-tokenizer` not set)**: Dynamo handles tokenization and passes `input_ids` to SGLang
- **With `--use-sglang-tokenizer`**: SGLang handles tokenization, Dynamo passes raw prompts
> **Note**: When using `--use-sglang-tokenizer`, only `v1/chat/completions` endpoints are available through Dynamo's frontend.
## SGLang Quick Start
Below we provide a guide that lets you run all of our common deployment patterns on a single node.
### Start NATS and ETCD in the background
Start using [Docker Compose](../../../deploy/docker-compose.yml)
- **Default (`--use-sglang-tokenizer` not set)**: Dynamo handles tokenization/detokenization via our blazing fast frontend and passes `input_ids` to SGLang
- **With `--use-sglang-tokenizer`**: SGLang handles tokenization/detokenization, Dynamo passes raw prompts
```bash
docker compose -f deploy/docker-compose.yml up -d
```
> [!NOTE]
> When using `--use-sglang-tokenizer`, only `v1/chat/completions` is available through Dynamo's frontend.
### Install `ai-dynamo[sglang]`
## Installation
#### Install latest release
### Install latest release
We suggest using uv to install the latest release of ai-dynamo[sglang]. You can install it with `curl -LsSf https://astral.sh/uv/install.sh | sh`
<details>
<summary>Expand for instructions</summary>
```bash
# create a virtual env
uv venv --python 3.12 --seed
# install the latest release
# install the latest release (which comes bundled with a stable sglang version)
uv pip install "ai-dynamo[sglang]"
```
#### Installing editable version for development
</details>
### Install editable version for development
<details>
<summary>Instructions</summary>
<summary>Expand for instructions</summary>
This requires having rust installed. We also recommend having a proper installation of the cuda toolkit as sglang requires `nvcc` to be available.
......@@ -119,40 +103,61 @@ maturin develop --uv
cd $DYNAMO_HOME
# installs sglang supported version along with dynamo
# include the prerelease flag to install flashinfer rc versions
uv pip install --prerelease=allow -e .[sglang]
uv pip install -e .
# install any sglang version >= 0.5.3
uv pip install "sglang[all]==0.5.3.post1"
```
</details>
#### Using prebuilt docker containers
### Using docker containers
<details>
<summary>Instructions</summary>
<summary>Expand for instructions</summary>
We are in the process of shipping pre-built docker containers that contain installations of DeepEP, DeepGEMM, and NVSHMEM in order to support WideEP and P/D. For now, you can quickly build the container from source with the following command.
```bash
docker pull nvcr.io/nvidia/ai-dynamo/sglang-runtime:my-tag
cd $DYNAMO_ROOT
docker build \
-f container/Dockerfile.sglang-wideep \
-t dynamo-sglang \
--no-cache \
.
```
</details>
#### Building docker container from source
<details>
<summary>Instructions</summary>
And then run it using
```bash
./container/build.sh --framework sglang
# run container using prebuild wheel
./container/run.sh --framework sglang -it
# mount workspace for development
./container/run.sh --framework sglang --mount-workspace
docker run \
--gpus all \
-it \
--rm \
--network host \
--shm-size=10G \
--ulimit memlock=-1 \
--ulimit stack=67108864 \
--ulimit nofile=65536:65536 \
--cap-add CAP_SYS_PTRACE \
--ipc host \
dynamo-sglang:latest
```
</details>
## Run Single Node Examples
## Quick Start
Below we provide a guide that lets you run all of our common deployment patterns on a single node.
### Start NATS and ETCD in the background
> [!IMPORTANT]
Start using [Docker Compose](../../../deploy/docker-compose.yml)
```bash
docker compose -f deploy/docker-compose.yml up -d
```
> [!TIP]
> Each example corresponds to a simple bash script that runs the OpenAI compatible server, processor, and optional router (written in Rust) and LLM engine (written in Python) in a single terminal. You can easily take each command and run them in separate terminals.
>
> Additionally - because we use sglang's argument parser, you can pass in any argument that sglang supports to the worker!
......@@ -167,15 +172,12 @@ cd $DYNAMO_HOME/components/backends/sglang
### Aggregated Serving with KV Routing
> [!NOTE]
> Until sglang releases a version > v0.5.0rc0, you will have to install from source to use kv_routing. You can do this by running `git clone https://github.com/sgl-project/sglang.git && cd sglang && uv pip install -e "python[all]"`. We will update this section once sglang releases a newer version.
```bash
cd $DYNAMO_HOME/components/backends/sglang
./launch/agg_router.sh
```
### Aggregated Serving with Embeddings
### Aggregated Serving for Embedding Models
Here's an example that uses the [Qwen/Qwen3-Embedding-4B](https://huggingface.co/Qwen/Qwen3-Embedding-4B) model.
......@@ -184,7 +186,8 @@ cd $DYNAMO_HOME/components/backends/sglang
./launch/agg_embed.sh
```
Send the following request to verify your deployment:
<details>
<summary>Send the following request to verify your deployment:</summary>
```bash
curl localhost:8000/v1/embeddings \
......@@ -195,28 +198,23 @@ curl localhost:8000/v1/embeddings \
}'
```
### Disaggregated serving
<details>
<summary>Under the hood: SGLang Load Balancer vs Dynamo Discovery</summary>
</details>
SGLang uses a mini load balancer to route requests to handle disaggregated serving. The load balancer functions as follows:
### Disaggregated serving
1. The load balancer receives a request from the client
2. A random `(prefill, decode)` pair is selected from the pool of available workers
3. Request is sent to both `prefill` and `decode` workers via asyncio tasks
4. Internally disaggregation is done from prefill -> decode
See [SGLang Disaggregation](sglang-disaggregation.md) to learn more about how sglang and dynamo handle disaggregated serving.
Because Dynamo has a discovery mechanism, we do not use a load balancer. Instead, we first route to a random prefill worker, select a random decode worker, and then send the request to both. Internally, SGLang's bootstrap server (which is a part of the `tokenizer_manager`) is used in conjuction with NIXL to handle the kv transfer.
</details>
```bash
cd $DYNAMO_HOME/components/backends/sglang
./launch/disagg.sh
```
> [!IMPORTANT]
> Disaggregated serving in SGLang currently requires each worker to have the same tensor parallel size [unless you are using an MLA based model](https://github.com/sgl-project/sglang/pull/5922)
### Disaggregated Serving with KV Aware Prefill Routing
```bash
cd $DYNAMO_HOME/components/backends/sglang
./launch/disagg.sh
./launch/disagg_router.sh
```
### Disaggregated Serving with Mixture-of-Experts (MoE) models and DP attention
......@@ -229,8 +227,6 @@ cd $DYNAMO_HOME/components/backends/sglang
./launch/disagg_dp_attn.sh
```
When using MoE models, you can also use the our implementation of the native SGLang endpoints to record expert distribution data. The `disagg_dp_attn.sh` script automatically sets up the SGLang HTTP server, the environment variable that controls the expert distribution recording directory, and sets up the expert distribution recording mode to `stat`. You can learn more about expert parallelism load balancing [here](expert-distribution-eplb.md).
### Testing the Deployment
Send a test request to verify your deployment:
......@@ -251,16 +247,6 @@ curl localhost:8000/v1/chat/completions \
}'
```
## Request Migration
You can enable [request migration](../../../docs/architecture/request_migration.md) to handle worker failures gracefully. Use the `--migration-limit` flag to specify how many times a request can be migrated to another worker:
```bash
python3 -m dynamo.sglang ... --migration-limit=3
```
This allows a request to be migrated up to 3 times before failing. See the [Request Migration Architecture](../../../docs/architecture/request_migration.md) documentation for details on how this works.
## Advanced Examples
Below we provide a selected list of advanced examples. Please open up an issue if you'd like to see a specific example!
......@@ -269,7 +255,7 @@ Below we provide a selected list of advanced examples. Please open up an issue i
- **[Run a multi-node model](multinode-examples.md)**
### Large scale P/D disaggregation with WideEP
- **[Run DeepSeek-R1 on 104+ H100s](dsr1-wideep-h100.md)**
- **[Run DeepSeek-R1-FP8 on H100s](dsr1-wideep-h100.md)**
- **[Run DeepSeek-R1-FP8 on GB200s](dsr1-wideep-gb200.md)**
### Hierarchical Cache (HiCache)
......
......@@ -17,21 +17,21 @@ limitations under the License.
# Running DeepSeek-R1 Disaggregated with WideEP on GB200s
Dynamo supports SGLang's GB200 implementation of wide expert parallelism and large scale P/D for DeepSeek-R1! You can read their blog post [here](https://lmsys.org/blog/2025-06-16-gb200-part-1/) for more details. Full end to end optimization is still a work in progress but you can get this up and running with the following steps. In ths example, we will run 1 prefill worker on 2 GB200 nodes (4 GPUs each) and 1 decode worker on 12 GB200 nodes (total 56 GPUs).
Dynamo supports SGLang's GB200 implementation of wide expert parallelism and large scale P/D for DeepSeek-R1! You can read their blog post [here](https://lmsys.org/blog/2025-06-16-gb200-part-1/) for more details. We provide a Dockerfile for this in `container/Dockerfile.sglang-wideep` and a sample configuration that demonstrates WideEP and P/D disaggregation. To run the exact configuration shown in the blog post, you can view the commands created by the SGLang team [here](https://github.com/sgl-project/sglang/issues/7227). In this example, we will run 1 prefill worker on 2 GB200 nodes (4 GPUs each) and 1 decode worker on 2 GB200 nodes (total 8 GPUs).
## Instructions
1. Build the Dynamo container
1. Build the Dynamo container using the latest published dynamo version and stable sglang version. If you want to build from a local dynamo repo, you can add `--build-arg BRANCH_TYPE=local` to the build command. If you want to build from a remote dynamo repo, you can add `--build-arg BRANCH_TYPE=remote` to the build command. If you want to use a specific tag for the default sglang version, you can add `--build-arg SGLANG_IMAGE_TAG=<tag>` to the build command.
> [!Note]
> Please ensure that you are building this on an ARM64 machine. The correct SGLang image will be selected automatically via the multi-arch manifest.
```bash
cd $DYNAMO_ROOT
docker build \
-f container/Dockerfile.sglang-wideep \
-t dynamo-wideep-gb200 \
--build-arg MODE=blackwell \
--build-arg SGLANG_IMAGE_TAG=v0.5.3rc0-cu129-gb200 \
--build-arg ARCH=arm64 \
--build-arg ARCH_ALT=aarch64 \
--no-cache \
.
```
......@@ -159,4 +159,4 @@ python3 -m dynamo.sglang \
--log-level debug
```
On the other decode nodes (this example has 12 total decode nodes), run the same command but change `--node-rank` to 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
On the other decode nodes (this example has 12 total decode nodes), run the same command but change `--node-rank` to 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
\ No newline at end of file
......@@ -5,19 +5,24 @@ SPDX-License-Identifier: Apache-2.0
# Running DeepSeek-R1 Disaggregated with WideEP on H100s
Dynamo supports SGLang's implementation of wide expert parallelism and large scale P/D for DeepSeek-R1! You can read their blog post [here](https://lmsys.org/blog/2025-05-05-large-scale-ep/) for more details. We provide a Dockerfile for this in `container/Dockerfile.sglang-wideep` and configurations to deploy this at scale. In this example, we will run 1 prefill worker on 4 H100 nodes and 1 decode worker on 9 H100 nodes (104 total GPUs).
Dynamo supports SGLang's implementation of wide expert parallelism and large scale P/D for DeepSeek-R1! You can read their blog post [here](https://lmsys.org/blog/2025-05-05-large-scale-ep/) for more details. We provide a Dockerfile for this in `container/Dockerfile.sglang-wideep` and a sample configuration that demonstrates WideEP and P/D disaggregation. To run the exact configuration shown in the blog post, you can view the commands created by the SGLang team [here](https://github.com/sgl-project/sglang/issues/6017). In this example, we will run 1 prefill worker on 4 H100 nodes and 1 decode worker on 4 H100 nodes (64 total GPUs).
## Instructions
1. Build the Dynamo container
1. Build the Dynamo container using the latest published dynamo version and stable sglang version. If you want to build from a local dynamo repo, you can add `--build-arg BRANCH_TYPE=local` to the build command. If you want to build from a remote dynamo repo, you can add `--build-arg BRANCH_TYPE=remote` to the build command. If you want to use a specific tag for the default sglang version, you can add `--build-arg SGLANG_IMAGE_TAG=<tag>` to the build command.
> [!Note]
> Please ensure that you are building this on an AMD64 (x86_64) machine. The correct SGLang image will be selected automatically via the multi-arch manifest.
```bash
cd $DYNAMO_ROOT
docker build -f container/Dockerfile.sglang-wideep . -t dynamo-wideep --no-cache
docker build \
-f container/Dockerfile.sglang-wideep \
-t dynamo-wideep \
--no-cache \
.
```
You can use a specific tag from the [lmsys dockerhub](https://hub.docker.com/r/lmsysorg/sglang/tags) by adding `--build-arg SGLANG_IMAGE_TAG=<tag>` to the build command.
2. You can run this container on each 8xH100 node using the following command.
> [!IMPORTANT]
......@@ -46,8 +51,6 @@ In each container, you should be in the `/sgl-workspace/dynamo/components/backen
```bash
# run ingress
python3 -m dynamo.frontend --http-port=8000 &
# optionally run the http server that allows you to flush the kv cache for all workers (see benchmarking section below)
python3 utils/sgl_http_server.py --ns dynamo &
# run prefill worker
python3 -m dynamo.sglang \
--model-path /model/ \
......@@ -62,8 +65,9 @@ python3 -m dynamo.sglang \
--tp-size 32 \
--dp-size 32 \
--enable-dp-attention \
--decode-log-interval 1 \
--enable-deepep-moe \
--decode-log-interval 1000 \
--moe-a2a-backend deepep \
--load-balance-method round_robin \
--page-size 1 \
--trust-remote-code \
--moe-dense-tp-size 1 \
......@@ -92,13 +96,14 @@ python3 -m dynamo.sglang \
--disaggregation-transfer-backend nixl \
--disaggregation-bootstrap-port 30001 \
--dist-init-addr ${HEAD_DECODE_NODE_IP}:29500 \
--nnodes 9 \
--nnodes 4 \
--node-rank 0 \
--tp-size 72 \
--dp-size 72 \
--tp-size 32 \
--dp-size 32 \
--enable-dp-attention \
--decode-log-interval 1 \
--enable-deepep-moe \
--decode-log-interval 1000 \
--moe-a2a-backend deepep \
--prefill-round-robin-balance \
--page-size 1 \
--trust-remote-code \
--moe-dense-tp-size 1 \
......@@ -112,59 +117,4 @@ python3 -m dynamo.sglang \
--cuda-graph-bs 128
```
On the other decode nodes (this example has 9 total decode nodes), run the same command but change `--node-rank` to 1, 2, 3, 4, 5, 6, 7, and 8
## Benchmarking
In the official [blog post repro instructions](https://github.com/sgl-project/sglang/issues/6017), SGL uses batch inference to benchmark their prefill and decode workers. They do this by pretokenizing the ShareGPT dataset and then creating a batch of 8192 requests with ISL 4096 and OSL 5 (for prefill stress test) and a batch of 40000 with ISL 2000 and OSL 100 (for decode stress test). If you want to repro these benchmarks, you will need to add the following flags to the prefill and decode commands:
prefill:
```bash
...
--max-running-requests 8192 \
--max-total-tokens 131072 \
--context-length 8192 \
--init-expert-location /configs/prefill_in4096.json \
--chunked-prefill-size 524288
```
decode:
```bash
...
--max-running-requests 18432 \
--context-length 4500 \
--init-expert-location /configs/decode_in2000out100.json
```
We currently provide 2 different ways to perform an end to end benchmark which includes using our OpenAI frontend and tokenization. We will continue to add better support for these sorts of large single batch workloads in the future.
1. **GenAI Perf to benchmark end to end performance with 8k ISL 256 OSL**
We've found that 8k ISL 256 OSL provides a good baseline for measuring end to end disaggregated serving performance for DSR1. As WideEP allows for a higher throughput, we provide a script that runs this workload at high concurrencies. DeepGEMM kernels can sometimes take a while to warm up. We provide a short ramping warmup script that can be used.
Example usage:
```bash
# warmup
./utils/bench.sh HEAD_PREFILL_NODE_IP --type warmup
# if you ran the http server on the head prefill node, you can optionally flush the kv cache for all workers (similar to SGLangs benchmarking script)
curl -X POST http://${HEAD_PREFILL_NODE_IP}:9001/flush_cache
# run benchmark
./utils/bench.sh HEAD_PREFILL_NODE_IP --type e2e
```
2. **GenAI Perf to benchmark completions with custom dataset**
We provide a script that generates a JSONL file of the ShareGPT dataset and then use GenAI Perf to benchmark the prefill and decode workers. We use ShareGPT in order to leverage the pre-existing EPLB distributions provided by the SGLang team. If you don't want to use ShareGPT - you can also use GenAI Perf's synthetic dataset setup But note you will have to use dynamic EPLB configurations or record your own as the `init-expert-location` provided by SGLang is tuned specifically for the ShareGPT dataset at a 4096 ISL and 5 OSL.
Example usage:
```bash
# generate data
python3 src/dynamo/sglang/utils/generate_bench_data.py --output data.jsonl --num-prompts 8192 --input-len 4096 --output-len 5 --model deepseek-ai/DeepSeek-R1
# if you ran the http server on the head prefill node, you can optionally flush the kv cache for all workers (similar to SGLangs benchmarking script)
curl -X POST http://${HEAD_PREFILL_NODE_IP}:9001/flush_cache
# run benchmark
./utils/bench.sh HEAD_PREFILL_NODE_IP --type custom_completions
```
On the other decode nodes (this example has 4 total decode nodes), run the same command but change `--node-rank` to 1, 2, and 3
......@@ -55,7 +55,7 @@ curl localhost:8000/v1/chat/completions \
Run the perf script:
```bash
bash -x /workspace/benchmarks/llm/perf.sh \
bash -x $DYNAMO_ROOT/benchmarks/llm/perf.sh \
--model Qwen/Qwen3-0.6B \
--tensor-parallelism 1 \
--data-parallelism 1 \
......
<!--
SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: Apache-2.0
-->
# SGLang Disaggregated Serving
This document explains how SGLang's disaggregated prefill-decode architecture works, both standalone and within Dynamo.
## Overview
Disaggregated serving separates the prefill and decode phases of LLM inference into different workers. This architecture allows for:
- Independent scaling of prefill and decode resources
- Better resource utilization (prefill is compute-bound, decode is memory-bound)
- Efficient KV cache transfer between workers using RDMA
## How Dynamo Integrates with SGLang Disaggregation
**SGLang's standalone approach:**
1. The load balancer receives a request from the client
2. A random `(prefill, decode)` pair is selected from the pool of available workers
3. Request is sent to both `prefill` and `decode` workers via asyncio tasks
4. Internally disaggregation is done from prefill → decode
**Dynamo's approach:**
Because Dynamo has a discovery mechanism, we do not use a load balancer. Instead:
1. Route to a decode worker first
2. Choose a prefill worker via round-robin or KV-aware selection
3. Send the request to both workers
4. SGLang's bootstrap server (part of the `tokenizer_manager`) is used in conjunction with NIXL/Mooncake to handle the KV transfer
## Disaggregation Flow
The following diagram shows the complete request flow for disaggregated serving:
```mermaid
sequenceDiagram
participant Client
participant Decode
participant Prefill
Note over Decode,Prefill: 0. Setup Phase (One-Time)
Decode->>Prefill: Register RDMA connection info (base GPU memory pointers)
Note over Client,Prefill: Per-Request Phase
Client->>Decode: 1. Send request
Decode->>Prefill: 2. Forward request + get bootstrap_room
Prefill-->>Decode: Return bootstrap_room ID
Note over Decode: 3. Allocate GPU memory for KV cache
Decode->>Prefill: Send allocation info (page indices, metadata buffer)
Note over Prefill: 4. Prefill forward pass
par Decode polls
loop Poll transfer
Note over Decode: 5. Poll for KV arrival
end
and Prefill transfers
Note over Prefill: 6. RDMA write KV to decode
Prefill->>Decode: Transfer KV cache + metadata
end
Note over Prefill: 7. Poll RDMA handles
Note over Prefill: Transfer complete, deallocate metadata
Note over Decode: 8. KV received, start decode
loop Generate tokens
Note over Decode: Decode forward pass
Decode-->>Client: Stream output token
end
```
### Key Steps Explained
**Setup Phase (One-Time)**
- Decode workers register their RDMA connection information with prefill workers
- This includes base GPU memory pointers for direct memory access
**Per-Request Flow**
1. **Request initiation**: Client sends request to decode worker
2. **Bootstrap room allocation**: Decode forwards to prefill and receives a bootstrap_room ID for coordination
3. **Memory allocation**: Decode allocates GPU memory pages for incoming KV cache
4. **Prefill execution**: Prefill worker processes the prompt and generates KV cache
5. **KV transfer**: Prefill uses RDMA to write KV cache directly to decode's GPU memory (while decode polls for completion)
6. **Cleanup**: Prefill deallocates transfer metadata after confirming completion
7. **Decode phase**: Decode worker generates tokens using the transferred KV cache
8. **Streaming**: Tokens are streamed back to the client as they're generated
### Performance Characteristics
- **RDMA transfer**: Zero-copy GPU-to-GPU transfer with minimal CPU involvement
- **Parallel operations**: Decode can poll while prefill transfers data
- **One-time setup**: RDMA connections established once, reused for all requests
\ No newline at end of file
......@@ -59,6 +59,7 @@
backends/sglang/gpt-oss.md
backends/sglang/multimodal_epd.md
backends/sglang/sgl-hicache-example.md
backends/sglang/sglang-disaggregation.md
examples/README.md
examples/runtime/hello_world/README.md
......
......@@ -60,7 +60,7 @@ vllm = [
sglang = [
"uvloop",
"nixl<=0.6.0",
"sglang[all]==0.5.3",
"sglang[all]==0.5.3.post1",
]
[dependency-groups]
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment