Repository
dspark-vllm-gx10
Two-node DGX Spark/ASUS GX10 DeepSeek V4 Flash DSpark NVFP4 port for vLLM 0.25, with live dashboard and reproducible deployment.
- Concurrency
- Previous runtime
- 1
- 40.7 tok/s
- 2
- 59.1 tok/s
- 4
- 91.4 tok/s
Specs
- ConcurrencyPrevious runtime
- 140.7 tok/s
- 259.1 tok/s
- 491.4 tok/s
- Input tokensvLLM 0.21.1
- 1,0241,778.7 tok/s
- 2,0481,990.5 tok/s
- 4,0962,083.1 tok/s
- 8,1922,049.8 tok/s
- 16,3842,052.6 tok/s
- 32,7681,901.1 tok/s
README
DSpark vLLM for two DGX Spark / ASUS GX10 nodes
This is a tested two-node GB10 port of the DeepSeek V4 Flash DSpark/NVFP4
serving path to vLLM 0.25.1. It bridges vLLM's DeepSeek V4 runtime to
FlashInfer's native SM120/SM121 sparse-MLA kernel, adds a b12x native-MXFP4 MoE
backend, and packages reproducible deployment, a live dashboard, version
switching, and benchmark evidence.
Validated configuration
- 2 × NVIDIA DGX Spark or ASUS Ascent GX10 (GB10, SM121, ARM64)
- dedicated high-speed fabric between nodes
- tensor parallelism: TP=2
- DeepSeek V4 Flash DSpark model using NVFP4 DS MLA KV cache
- vLLM source tag
v0.25.1; runtime reports
0.25.2.dev0+g752a3a504.d20260714 - FlashInfer pinned to
0472b9b3f2fba11b463f8526f390297d52a8aad7 - b12x pinned to
7dc6fb8fcc6446ea093537d1657df81985fa5f43
What this port changes
- adds
nvfp4_ds_mlaas a first-class DeepSeek V4 KV-cache format throughout
vLLM configuration, quantization, and cache-size accounting; - uses the tested 584-byte packed sparse-MLA token envelope for both MLA and
sliding-window cache groups; - adapts vLLM's FlashInfer SM120/SM121 wrapper to split oversized 256-token SWA
pages into zero-copy 64-token views while preserving compressed C128 pages; - supports TP=2's 32 query heads and pads unsupported sparse-index widths to
FlashInfer's native 128/512/1024 dispatch widths with invalid-slot sentinels; - adds a modular b12x MXFP4 MoE backend with native weight preparation,
caller-owned scratch, CUDA-graph-safe execution, GB10 small-M tuning, and
startup route-pack specialization warmup; - adds two-node Compose/start/update tooling plus the separate real-time
dashboard and controlled performance harness.
The exact file-level implementation is described in
docs/implementation.md.
Model weights are not included. Set DSPARK_MODEL_HOST to a model directory
you are licensed to use.
Install
Clone this repository on both nodes:
git clone https://github.com/anemll/dspark-vllm-gx10.git
cd dspark-vllm-gx10
./scripts/install.sh --role worker
./scripts/install.sh --role head
Edit config/worker.env and config/head.env. Replace every CHANGEME value,
verify the model/cache paths, and use the dedicated fabric addresses—not Wi-Fi
or the general LAN.
Start rank 1 first, then rank 0:
# Worker
./scripts/start-node.sh config/worker.env
# Wait until rank 1 is listening for the rendezvous, then on the head:
./scripts/start-node.sh config/head.env
The head API is available at http://HEAD_HOST:8888. A successful startup
returns HTTP 200 from /health and the runtime string from /version.
Dashboard
The dashboard is a dependency-free Python service. It displays decode/prefill
throughput, active non-zero averages, token totals, DSpark acceptance, request
latency, load state for both TP ranks, vLLM version, temperature, power, GPU
utilization, and optional NVMe temperature.
See docs/dashboard.md for installation and configuration.
Update
Pull the repository and prepare a new image tag on each node:
git pull --ff-only
./scripts/update.sh 0.1.1 config/worker.env
./scripts/update.sh 0.1.1 config/head.env
Restart rank 1 first and rank 0 second. The updater preserves a timestamped
backup of the previous environment file.
Build from source
The prebuilt ARM64 image is published as:
ghcr.io/anemll/dspark-vllm-gx10:0.1.1
To reproduce it locally, run ./scripts/build-image.sh. The script checks out
the exact vLLM commit in upstream.lock, applies overlay/, builds the vLLM
ARM64 image, and installs pinned FlashInfer and b12x Git revisions. The b12x
pin is intentionally a Git commit: the tested 0.15.3 source was not released
on PyPI.
docker/Dockerfile.promote-tested is a maintainer-only release step that adds
OCI source/revision labels and bundled license notices to an image that has
already passed the two-node validation. It does not replace the reproducible
source build above and does not change runtime code.
Performance
Benchmark model:
- drowzeys/DeepSeek-V4-Flash-DSpark-Abliterated-Uncensored
- DeepSeek V4 Flash, 284B MoE / approximately 13B active parameters
- FP8 weights (E4M3, UE8M0 scales, 128x128 blocks): 48 Safetensors
shards totaling 166,886,535,336 bytes (155.43 GiB) on each node nvfp4_ds_mlaKV cache; NVFP4 describes the DS-MLA cache format, not the
checkpoint's FP8 weight format- TP=2 across two GX10 nodes; server maximum context 350,000 tokens
Single-node reference: the unchanged checkpoint is not runnable on one
GX10. Its 155.43 GiB of weight files exceed the node's approximately 121 GiB
of usable unified memory before KV cache and runtime allocations. A controlled
TP=1 launch reached NVIDIA NV_ERR_NO_MEMORY before the API became ready, so
there are no valid single-node throughput samples. The full
fit-check record is kept
with the benchmark results.
Best aggregate output throughput from the controlled 512-token workload:
| Concurrency | Previous runtime | vLLM 0.25 candidate | Gain |
|---|---|---|---|
| 1 | 40.7 tok/s | 48.5 tok/s | 19.1% |
| 2 | 59.1 tok/s | 70.4 tok/s | 19.2% |
| 4 | 91.4 tok/s | 103.5 tok/s | 13.2% |
Raw results and the dependency-free client are in benchmarks/.
The post-fix
route-pack warmup validation
includes strict-JIT boundary coverage, a 65K prefill check, and decode
regression results.
Warmed, server-side prefill results on the same two-node TP=2 deployment:
| Input tokens | vLLM 0.21.1 | vLLM 0.25 candidate | Gain |
|---|---|---|---|
| 1,024 | 1,778.7 tok/s | 2,033.0 tok/s | 14.3% |
| 2,048 | 1,990.5 tok/s | 2,252.0 tok/s | 13.1% |
| 4,096 | 2,083.1 tok/s | 2,320.7 tok/s | 11.4% |
| 8,192 | 2,049.8 tok/s | 2,184.2 tok/s | 6.6% |
| 16,384 | 2,052.6 tok/s | 2,203.8 tok/s | 7.4% |
| 32,768 | 1,901.1 tok/s | 2,176.1 tok/s | 14.5% |
The comparison and
raw reports contain TTFT and per-trial details.
Prefill is measured at exact 1K, 2K, 4K, 8K, 16K, and 32K input lengths.
The harness records client TTFT plus vLLM's server-side prefill duration and
computed-token count. It uses reproducible, unique token-ID prompts so the
before and after runs receive identical input without prefix-cache reuse.
An initial warm-up plus one excluded pass at every tested input length prevents
first-shape compilation from contaminating the three-trial medians:
# Run against the previous runtime, then switch the two-node server version.
python3 benchmarks/benchmark_prefill.py --label before \
--output benchmarks/results/prefill-before.json
# Run the identical matrix against the candidate runtime.
python3 benchmarks/benchmark_prefill.py --label after \
--output benchmarks/results/prefill-after.json
python3 benchmarks/compare_prefill.py \
benchmarks/results/prefill-before.json \
benchmarks/results/prefill-after.json
Run these tests on an otherwise idle server. The harness detects overlapping
requests and excludes contaminated server-side trials from its median.
Important operational note
Start the worker before the head. Starting both simultaneously can leave the
distributed initialization waiting on TCPStore/NCCL. The provided scripts do
not store passwords or SSH credentials.
Licensing and attribution
Repo-local dashboard, deployment, documentation, and benchmark work is MIT
licensed under LICENSE. The vLLM-derived files under overlay/
remain Apache-2.0; the complete Apache text is included at
LICENSES/Apache-2.0.txt.
See CREDITS.md for exact dependency revisions and explicit credit
to vLLM, FlashInfer, Luke Alonso/b12x, voipmonitor, Keys/drowzeys, Rafael
Caricio, MiaAI-Lab, TonyD2Wild, Fraser Price, and roady001. Model weights are
not included or relicensed.