Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
8cf10ab
Add DeepSeek-V4-Pro FP4 B200 llmd-vllm P/D disagg (agentX 1P-DEP8/1D-…
ilmarkov Aug 24, 2026
83c3060
Trim disagg conc-list to 192 and script the v0.10.0 EPP binary pull
ilmarkov Aug 26, 2026
fe7e680
Add DeepSeek-V4-Pro FP4 B200 llmd-vllm aggregated (TP8/DEP8) benchmark
ilmarkov Aug 26, 2026
79f3e12
Update configs
ilmarkov Aug 31, 2026
839c220
Merge upstream/main into imarkov/dsv4-pro-agentic
ilmarkov Aug 31, 2026
8136823
Cleanup comments
ilmarkov Aug 31, 2026
af3818d
Address review comments
ilmarkov Aug 31, 2026
e648883
Update DSV4-Pro model to 0813 version
ilmarkov Aug 31, 2026
a4edd45
fix: run llm-d DSpark with AgentX golden acceptance and explicit meta…
cquil11 Sep 2, 2026
a437eb1
merge: resolve main conflicts while preserving DSpark AgentX fixes
cquil11 Sep 2, 2026
e3d6d0c
fix: accept cluster runner labels in llm-d container selection
cquil11 Sep 2, 2026
8d00f59
chore: remove standalone llm-d recipe and redundant config tests
cquil11 Sep 2, 2026
0391477
docs: consolidate DSpark performance changelog
cquil11 Sep 2, 2026
a65c1e3
fix: expose and verify llm-d worker metrics for AIPerf
cquil11 Sep 2, 2026
f6cfad9
fix(llm-d): use the B200 Nscale shared home for cached artifacts
cquil11 Sep 2, 2026
1758eac
fix(llm-d): match AgentX router metadata to the bundled version
cquil11 Sep 2, 2026
4e5f6c6
fix(llm-d): move AgentX to GB200 and simplify launch paths
cquil11 Sep 2, 2026
775e5e9
Adapt optimized configs and map to agentic gb200
ilmarkov Sep 2, 2026
07c6cfd
merge: resolve conflicts with upstream/imarkov/dsv4-pro-agentic
ilmarkov Sep 2, 2026
95586e0
Fix recipe
ilmarkov Sep 2, 2026
6365015
Fix spec decoding
ilmarkov Sep 2, 2026
11b8af5
Address cursor's comment
ilmarkov Sep 2, 2026
81060c2
fix: keep GB200 import setup explicit and correct DSpark changelog
cquil11 Sep 3, 2026
e8e1594
merge: sync main while preserving the GB200 AgentX changelog entry
cquil11 Sep 3, 2026
8493a29
fix: preserve fixed DSpark draft budgets for golden AL
cquil11 Sep 3, 2026
6394609
fix: use GB200 InfiniBand adapters for Mooncake transfers
cquil11 Sep 3, 2026
73476be
fix: provide DEP8 router token-load dependencies
cquil11 Sep 3, 2026
3ba226b
fix: start a shared Mooncake master for llm-d jobs
cquil11 Sep 3, 2026
bc55cca
fix: reserve GPU memory for long-context llm-d replay
cquil11 Sep 3, 2026
9c1c0c4
fix: cover TP8 and DEP8 long-context allocation peaks
cquil11 Sep 3, 2026
7b65775
fix: prevent duplicate llm-d frontend metric scrapes
cquil11 Sep 3, 2026
4a6231e
fix: retain P/D KV blocks across long decode stalls
cquil11 Sep 3, 2026
9411ed9
fix: validate llm-d metrics targets and retain endpoint roles
cquil11 Sep 3, 2026
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
33 changes: 33 additions & 0 deletions benchmarks/llm-d/binaries-b200-v0.10.0.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# B200-only binary pins for the agentX-ported EPP config used by
# dsv4-fp4-b200-agentx-1p1d-dep8-dep8.yaml (disagg-profile-handler
# `deciders:` shape + inflight-load-producer / approx-prefix-cache-producer /
# prefix-cache-affinity-filter / token-load-scorer plugins), which require
# the v0.10.0 EPP/pd-sidecar router build. Kept as a sibling file (not a
# change to binaries.env) so GB200's shared v0.9.0 pins and bin path are
# completely untouched.
#
# Pull with extract-binaries.sh instead of binaries.env's defaults:
# BINARIES_ENV_FILE=binaries-b200-v0.10.0.env \
# benchmarks/llm-d/extract-binaries.sh
# Run once (or whenever these pins change) on a host with docker + registry
# access and the /home/sa-shared filesystem mounted.

# --- Endpoint Picker (EPP / inference scheduler) ---
EPP_FROM_IMAGE="ghcr.io/llm-d/llm-d-router-endpoint-picker:v0.10.0"
EPP_BIN_PATH="/app/epp"

# --- P/D routing sidecar (pd-sidecar) ---
ROUTING_SIDECAR_IMAGE="ghcr.io/llm-d/llm-d-router-disagg-sidecar:v0.10.0"
ROUTING_SIDECAR_BIN_PATH="/app/pd-sidecar"

# --- Envoy front proxy ---
# Unchanged from binaries.env; re-declared here so this file is
# self-contained (extract-binaries.sh always extracts all three).
ENVOY_FROM_IMAGE="envoyproxy/envoy:distroless-v1.33.2"
ENVOY_BIN_PATH="/usr/local/bin/envoy"

# B200 is x86_64 (unlike GB200's Grace/arm64, binaries.env's default).
LLMD_BIN_PLATFORM="${LLMD_BIN_PLATFORM:-linux/amd64}"

# Dedicated path so this never collides with GB200's shared bin dir.
LLMD_BIN_DIR="${LLMD_BIN_DIR:-/home/sa-shared/llm-d-bins-v0.10.0}"
9 changes: 8 additions & 1 deletion benchmarks/llm-d/extract-binaries.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,19 @@
# ./extract-binaries.sh # uses binaries.env defaults
# LLMD_BIN_DIR=/some/dir ./extract-binaries.sh
# LLMD_BIN_PLATFORM=linux/amd64 ./extract-binaries.sh # for an x86 test
# BINARIES_ENV_FILE=binaries-b200-v0.10.0.env ./extract-binaries.sh
# # pull a different set of image pins (e.g. a router-version bump
# # scoped to one cluster) without touching the shared binaries.env

set -euo pipefail

HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BINARIES_ENV_FILE="${BINARIES_ENV_FILE:-$HERE/binaries.env}"
# Resolve a bare filename against this script's directory, so
# BINARIES_ENV_FILE=foo.env works regardless of the caller's cwd.
[[ "$BINARIES_ENV_FILE" != /* ]] && BINARIES_ENV_FILE="$HERE/$BINARIES_ENV_FILE"
# shellcheck source=/dev/null
source "$HERE/binaries.env"
source "$BINARIES_ENV_FILE"

echo "Extracting llm-d binaries -> $LLMD_BIN_DIR (platform $LLMD_BIN_PLATFORM)"
mkdir -p "$LLMD_BIN_DIR"
Expand Down
64 changes: 64 additions & 0 deletions benchmarks/multi_node/dsv4_fp4_b200_llmd-vllm-agg.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#!/usr/bin/env bash
#
# Wrapper for the DeepSeek-V4-Pro B200 llmd-vllm aggregated benchmark
# (agentX-flavored TP8 / DEP8, one engine does both prefill and decode).
# Sibling of dsv4_fp4_b200_llmd-vllm-disagg.sh - same shape, but always
# submits with DECODE_NODES=0 (no decode role at all; see the aggregated
# recipes' header comments and server.sh's IS_AGGREGATED handling). The
# runner resolves this script via
# SCRIPT_NAME="${EXP_NAME%%_*}_${PRECISION}_b200_llmd-vllm-agg.sh"
# from launch_b200-dgxc-slurm.sh when DISAGG=false.

set -euo pipefail

source "$(dirname "$0")/../benchmark_lib.sh"

check_env_vars \
CONC_LIST \
ISL \
OSL \
IMAGE \
MODEL_PATH \
PREFILL_NODES \
DECODE_NODES \
RANDOM_RANGE_RATIO

if [[ -n "${SLURM_JOB_ID:-}" ]]; then
echo "JOB $SLURM_JOB_ID running on $SLURMD_NODENAME"
fi

if [[ "$DECODE_NODES" != "0" ]]; then
echo "Error: dsv4_fp4_b200_llmd-vllm-agg.sh requires DECODE_NODES=0 (got $DECODE_NODES); aggregated mode has no decode role" >&2
exit 1
fi

set -x

cd "$GITHUB_WORKSPACE/benchmarks/multi_node/llm-d" || exit 1

# B200 DGX = 8 GPUs per node (submit.sh defaults to 8, explicit for clarity).
export GPUS_PER_NODE="${GPUS_PER_NODE:-8}"

export TIME_LIMIT="${TIME_LIMIT:-08:00:00}"
export MODEL_PATH=$MODEL_PATH
export MODEL_NAME=$MODEL_NAME
export CONTAINER_IMAGE=$IMAGE

# Aggregated is always a single engine (no multi-engine high-tpt split), so
# PREFILL_WORKERS is always 1. DECODE_WORKERS is unused (DECODE_NODES=0) but
# still exported since submit.sh/server.sh read it unconditionally.
export PREFILL_WORKERS="${PREFILL_WORKERS:-1}"
export DECODE_WORKERS="${DECODE_WORKERS:-1}"

JOB_ID=$(bash ./submit.sh \
"$PREFILL_NODES" \
"$DECODE_NODES" \
"$ISL" "$OSL" "${CONC_LIST// /x}" inf \
"$RANDOM_RANGE_RATIO")

if [[ -z "$JOB_ID" ]]; then
echo "Failed to submit job" >&2
exit 1
fi

echo "$JOB_ID"
60 changes: 60 additions & 0 deletions benchmarks/multi_node/dsv4_fp4_b200_llmd-vllm-disagg.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#!/usr/bin/env bash
#
# Wrapper for the DeepSeek-V4-Pro B200 llmd-vllm P/D disagg benchmark
# (agentX-flavored 1P-DEP8/1D-DEP8). Sibling of
# dsv4_fp4_gb200_llmd-vllm-disagg.sh - same shape, different topology
# (B200 = 8 GPUs/node, so each DEP8 role fits on ONE node; GB200 = 4
# GPUs/node, role spans 2 nodes). The runner resolves this script via
# SCRIPT_NAME="${EXP_NAME%%_*}_${PRECISION}_b200_llmd-vllm-disagg.sh"
# from launch_b200-dgxc-slurm.sh.

set -euo pipefail

source "$(dirname "$0")/../benchmark_lib.sh"

check_env_vars \
CONC_LIST \
ISL \
OSL \
IMAGE \
MODEL_PATH \
PREFILL_NODES \
DECODE_NODES \
RANDOM_RANGE_RATIO

if [[ -n "${SLURM_JOB_ID:-}" ]]; then
echo "JOB $SLURM_JOB_ID running on $SLURMD_NODENAME"
fi

set -x

cd "$GITHUB_WORKSPACE/benchmarks/multi_node/llm-d" || exit 1

# B200 DGX = 8 GPUs per node (submit.sh defaults to 8, explicit for clarity).
export GPUS_PER_NODE="${GPUS_PER_NODE:-8}"

export TIME_LIMIT="${TIME_LIMIT:-08:00:00}"
export MODEL_PATH=$MODEL_PATH
export MODEL_NAME=$MODEL_NAME
export CONTAINER_IMAGE=$IMAGE

# Worker count per role (Option B multi-engine). Prefer an explicit
# PREFILL_WORKERS/DECODE_WORKERS from the matrix additional-settings; else
# fall back to the matrix num-worker fields (PREFILL_NUM_WORKERS/
# DECODE_NUM_WORKERS); else 1 (single engine = unchanged 1P+1D). submit.sh
# reads these.
export PREFILL_WORKERS="${PREFILL_WORKERS:-${PREFILL_NUM_WORKERS:-1}}"
export DECODE_WORKERS="${DECODE_WORKERS:-${DECODE_NUM_WORKERS:-1}}"

JOB_ID=$(bash ./submit.sh \
"$PREFILL_NODES" \
"$DECODE_NODES" \
"$ISL" "$OSL" "${CONC_LIST// /x}" inf \
"$RANDOM_RANGE_RATIO")

if [[ -z "$JOB_ID" ]]; then
echo "Failed to submit job" >&2
exit 1
fi

echo "$JOB_ID"
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# DeepSeek-V4-Pro (FP4) on B200, aggregated DEP8 (one engine does both
# prefill and decode) via llmd-vllm. agentX-flavored sibling of
# dsv4-fp4-b200-agentx-agg-tp8.yaml (same aggregated wiring, DP+EP instead
# of pure TP).
#
# Topology: B200 = 8 GPUs/node, so DEP8 (DP=8, EP on) fits on exactly ONE
# node (unlike GB200's 4 GPUs/node, where agentX's ix-agg-dep-base.yaml/
# agg-dep8.yaml needs `lws: {size: 2, replicas: 1}` spanning 2 nodes via
# hybrid-lb). PREFILL_NODES=1 DECODE_NODES=0 GPUS_PER_NODE=8 -> 1 node / 8
# GPUs total. Since the role never spans >1 node, server.sh's
# LWS_GROUP_SIZE is always 1 here, so the cross-node --data-parallel-hybrid-lb
# / NVSHMEM-ibgda code paths never activate - vLLM runs its DP=8 ranks
# in-process on the one node.
#
# Aggregated-mode wiring: see the header comment in
# dsv4-fp4-b200-agentx-agg-tp8.yaml (no decode role / no pd-sidecar / no
# kv_transfer_config, engine mapped to the "prefill" role, rank 0 doubles
# as EPP/Envoy/bench coordinator). The EPP config below is identical in
# shape to that recipe's (same rationale: agentX's aggregated K8s config has
# no llm-d EPP schema to port, so this is a purpose-built minimal
# single-profile config, no v0.10.0 router bump needed).
#
# Source (agentX): llm-manifesto/models/deepseek-v4-ix/ix-agg-dep-base.yaml
# + agg-dep8.yaml. Dropped entirely (no InferenceX equivalent needed):
# the `mooncake:` sidecar block and the MultiConnector
# NixlConnector+SimpleCPUOffloadConnector+MooncakeStoreConnector KV chain
# (aggregated has no P/D KV transfer at all, see above), and the
# Mooncake/UCX-specific env (VLLM_MOONCAKE_LOAD_RECV_THREADS,
# MC_ENABLE_DEST_DEVICE_AFFINITY, UCX_TLS=rc,cuda_copy) that existed only to
# support that KV chain. max_model_len trimmed to 9280 (ISL 8192 + OSL 1024
# + headroom) instead of agentX's general-purpose 1048576, matching every
# other recipe in this directory.
apiVersion: llm-d.ai/v1alpha1
kind: EndpointPickerConfig

plugins:
- name: file-disc
type: file-discovery
parameters:
path: /tmp/endpoints.yaml
watchFile: false

- type: prefill-filter
- type: queue-scorer
- type: kv-cache-utilization-scorer
- type: active-request-scorer
- type: prefix-cache-scorer

schedulingProfiles:
- name: default
plugins:
- pluginRef: prefill-filter
- pluginRef: queue-scorer
weight: 2
- pluginRef: kv-cache-utilization-scorer
weight: 2
- pluginRef: active-request-scorer
weight: 1
- pluginRef: prefix-cache-scorer
weight: 3

dataLayer:
discovery:
pluginRef: file-disc

# ---- Per-role vLLM flags ----
# Ported from agentX's ix-agg-dep-base.yaml (roles[0], extended by
# agg-dep8.yaml). DEP8 (TP=1, DP=8, EP on), FLASHINFER_MLA_SPARSE_DSV4
# attention, deep_gemm_mega_moe MoE backend, EP weight filter (keeps only
# served experts resident - required for DEP8 to avoid OOM), matching the
# disagg recipe's DEP8 roles. tp/enable-expert-parallel are explicit here
# even though they match server.sh's defaults, for self-documentation.
#
# No `decode:` block: server.sh only ever reads the section named after the
# node's own ROLE, which is always "prefill" for this recipe (DECODE_NODES=0).
prefill:
tp: 1
enable-expert-parallel: true
extra-args: >-
--kv-cache-dtype fp8
--gpu-memory-utilization 0.92
--max-model-len 9280
--max-num-seqs 64
--max-num-batched-tokens 8192
--max-cudagraph-capture-size 64
--block-size 256
--tokenizer-mode deepseek_v4
--moe-backend deep_gemm_mega_moe
--enable-ep-weight-filter
--enable-cumem-allocator
--no-disable-hybrid-kv-cache-manager
--no-enable-flashinfer-autotune
--numa-bind
--compilation-config {"cudagraph_mode":"FULL_DECODE_ONLY","mode":0}
--speculative-config {"method":"dspark","num_speculative_tokens":5,"draft_sample_method":"probabilistic","attention_backend":"FLASHINFER_MLA_SPARSE_DSV4","enable_adaptive_verification":true}
--attention-config {"backend":"FLASHINFER_MLA_SPARSE_DSV4","use_prefill_query_quantization":true,"use_fp4_indexer_cache":true}
env:
VLLM_USE_RUST_FRONTEND: "1"
VLLM_SERVER_DEV_MODE: "1"
VLLM_ENGINE_READY_TIMEOUT_S: "3600"
VLLM_RPC_TIMEOUT: "600000"
VLLM_PREFIX_CACHE_RETENTION_INTERVAL: "32768"
VLLM_DSV4_MEGA_FP8_COMBINE: "1"
VLLM_EXECUTE_MODEL_TIMEOUT_SECONDS: "1800"
VLLM_RANDOMIZE_DP_DUMMY_INPUTS: "1"
TILELANG_CLEANUP_TEMP_FILES: "1"
# Differs from agg-tp8.yaml's "0" - ported as-is from
# ix-agg-dep-base.yaml (DEP8's DP-ranks-as-processes path apparently
# wants NCCL symmetric memory enabled, unlike pure TP8's allreduce path).
VLLM_USE_NCCL_SYMM_MEM: "1"

# ---- SLURM resource directives ----
slurm:
time_limit: "08:00:00"
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# DeepSeek-V4-Pro (FP4) on B200, aggregated DEP8 with Mooncake prefix-cache
# KV store enabled. Sibling of agg-gb200-dep8-dspark-agentic.yaml; adds a
# `mooncake:` section so server.sh wires a MultiConnector
# (NixlConnector + MooncakeStoreConnector) for cross-run prefix-cache sharing
# via RDMA between DP ranks. See server.sh's aggregated+Mooncake branch for
# the kv_both role assignment.
#
# EPP config and per-role vLLM flags are identical to the base dep8 recipe.
# Mooncake store config (P2PHANDSHAKE embedded mode, 140 GB segment) matches
# the disagg-gb200-1p1d-dep8-dep8-dspark-agentic.yaml recipe.
apiVersion: llm-d.ai/v1alpha1
kind: EndpointPickerConfig

plugins:
- name: file-disc
type: file-discovery
parameters:
path: /tmp/endpoints.yaml
watchFile: false

- type: prefill-filter
- type: queue-scorer
- type: kv-cache-utilization-scorer
- type: active-request-scorer
- type: prefix-cache-scorer

schedulingProfiles:
- name: default
plugins:
- pluginRef: prefill-filter
- pluginRef: queue-scorer
weight: 2
- pluginRef: kv-cache-utilization-scorer
weight: 2
- pluginRef: active-request-scorer
weight: 1
- pluginRef: prefix-cache-scorer
weight: 3

dataLayer:
discovery:
pluginRef: file-disc

# ---- Per-role vLLM flags ----
prefill:
tp: 1
enable-expert-parallel: true
extra-args: >-
--kv-cache-dtype fp8
--gpu-memory-utilization 0.92
--max-model-len 9280
--max-num-seqs 64
--max-num-batched-tokens 8192
--max-cudagraph-capture-size 64
--block-size 256
--tokenizer-mode deepseek_v4
--moe-backend deep_gemm_mega_moe
--enable-ep-weight-filter
--enable-cumem-allocator
--no-disable-hybrid-kv-cache-manager
--no-enable-flashinfer-autotune
--numa-bind
--compilation-config {"cudagraph_mode":"FULL_DECODE_ONLY","mode":0}
--speculative-config {"method":"dspark","num_speculative_tokens":5,"draft_sample_method":"probabilistic","attention_backend":"FLASHINFER_MLA_SPARSE_DSV4","enable_adaptive_verification":true}
--attention-config {"backend":"FLASHINFER_MLA_SPARSE_DSV4","use_prefill_query_quantization":true,"use_fp4_indexer_cache":true}
env:
VLLM_USE_RUST_FRONTEND: "1"
VLLM_SERVER_DEV_MODE: "1"
VLLM_ENGINE_READY_TIMEOUT_S: "3600"
VLLM_RPC_TIMEOUT: "600000"
VLLM_PREFIX_CACHE_RETENTION_INTERVAL: "32768"
VLLM_DSV4_MEGA_FP8_COMBINE: "1"
VLLM_EXECUTE_MODEL_TIMEOUT_SECONDS: "1800"
VLLM_RANDOMIZE_DP_DUMMY_INPUTS: "1"
TILELANG_CLEANUP_TEMP_FILES: "1"
VLLM_USE_NCCL_SYMM_MEM: "1"
VLLM_MOONCAKE_LOAD_RECV_THREADS: "20"
MC_ENABLE_DEST_DEVICE_AFFINITY: "1"
MC_STORE_CLIENT_METRIC: "1"
MC_STORE_CLIENT_METRIC_INTERVAL: "5"
MC_TE_METRIC: "0"

# ---- Mooncake KV store config ----
mooncake:
store_config:
metadata_server: "P2PHANDSHAKE"
global_segment_size: "140GB"
local_buffer_size: "4GB"
protocol: "rdma"
device_name: "mlx5_0,mlx5_1,mlx5_2,mlx5_3"
mode: "embedded"
enable_offload: false

# ---- SLURM resource directives ----
slurm:
time_limit: "08:00:00"
Loading
Loading