From 6efd246c81a3f129b16bb6d665bca1dc0e666fcd Mon Sep 17 00:00:00 2001 From: Jeff Ma Date: Fri, 17 Jul 2026 00:56:07 -0700 Subject: [PATCH 01/11] fix(changelog): accept single-node agentic eval rows in matrix validation #1947 made single-node agentic recipes generate a SWE-bench eval row (run_eval/eval_only + agentic fields) but never widened the changelog matrix schema, so ChangelogMatrixEntry.evals (typed list[SingleNodeMatrixEntry], fixed-seq-len only) rejects every agentic eval row -- breaking check-changelog for any single-node agentic PR. Widen evals to the same Union single_node already uses, and give SingleNodeAgenticMatrixEntry optional run_eval/eval_only (None-default, so benchmark rows are unchanged). Co-Authored-By: Claude Opus 4.8 (1M context) --- utils/matrix_logic/validation.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/utils/matrix_logic/validation.py b/utils/matrix_logic/validation.py index 6b7c671289..7403c795e4 100644 --- a/utils/matrix_logic/validation.py +++ b/utils/matrix_logic/validation.py @@ -291,6 +291,10 @@ class SingleNodeAgenticMatrixEntry(BaseModel): duration: int = Field(alias=Fields.DURATION.value) exp_name: str = Field(alias=Fields.EXP_NAME.value) scenario_type: str = Field(alias=Fields.SCENARIO_TYPE.value) + # Agentic eval rows (SWE-bench) carry run_eval/eval_only; benchmark rows omit + # them. Optional so both shapes validate and benchmark output is unchanged. + run_eval: Optional[bool] = Field(default=None, alias=Fields.RUN_EVAL.value) + eval_only: Optional[bool] = Field(default=None, alias=Fields.EVAL_ONLY.value) @model_validator(mode='after') def validate_kv_offload_fields(self): @@ -889,7 +893,7 @@ class ChangelogMatrixEntry(BaseModel): ] = Field(default_factory=dict) multi_node: dict[str, list[Union[MultiNodeMatrixEntry, MultiNodeAgenticMatrixEntry]] ] = Field(default_factory=dict) - evals: list[SingleNodeMatrixEntry] = Field(default_factory=list) + evals: list[Union[SingleNodeMatrixEntry, SingleNodeAgenticMatrixEntry]] = Field(default_factory=list) multinode_evals: list[MultiNodeMatrixEntry] = Field(default_factory=list) changelog_metadata: ChangelogMetadata From a53a365dbc9fde13ab1520c036ba36a09be01865 Mon Sep 17 00:00:00 2001 From: Jeff Ma Date: Fri, 17 Jul 2026 01:11:10 -0700 Subject: [PATCH 02/11] [AgentX] DeepSeek-V4 B200 vLLM: add MTP (num_speculative_tokens=3) for every agentic arm Add an MTP speculative-decoding twin (spec-decoding: mtp, num_speculative_tokens=3) for every arm of dsv4-fp4-b200-vllm-agentic (TP8 GPU-resident, TP8 SimpleCPU, DEP8 SimpleCPU, DEP8 Mooncake), each mirroring its non-MTP conc-list, routed via the launcher's spec-decoding=mtp suffix to dsv4_fp4_b200_vllm_mtp.sh. New dsv4_fp4_b200_vllm_mtp.sh forks dsv4_fp4_b200_vllm.sh with only the MTP deltas: --speculative-config {"method":"mtp","num_speculative_tokens":3} and --max-cudagraph-capture-size scaled to MAX_NUM_SEQS*(1+N) tokens so FULL_DECODE_ONLY still covers the largest decode batches. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../agentic/dsv4_fp4_b200_vllm_mtp.sh | 302 ++++++++++++++++++ configs/nvidia-master.yaml | 8 + 2 files changed, 310 insertions(+) create mode 100755 benchmarks/single_node/agentic/dsv4_fp4_b200_vllm_mtp.sh diff --git a/benchmarks/single_node/agentic/dsv4_fp4_b200_vllm_mtp.sh b/benchmarks/single_node/agentic/dsv4_fp4_b200_vllm_mtp.sh new file mode 100755 index 0000000000..391a047a71 --- /dev/null +++ b/benchmarks/single_node/agentic/dsv4_fp4_b200_vllm_mtp.sh @@ -0,0 +1,302 @@ +#!/usr/bin/env bash +set -eo pipefail +set -x + +# Agentic trace replay benchmark for DeepSeek-V4-Pro FP4 on B200 using vLLM, +# with MTP speculative decoding (num_speculative_tokens=3). +# +# Identical to dsv4_fp4_b200_vllm.sh (same image, engine args, offload, GPU +# topologies, and agentic aiperf rig) with exactly two MTP deltas: +# --speculative-config '{"method": "mtp", "num_speculative_tokens": 3}' +# --max-cudagraph-capture-size expressed in TOKENS (see the capture block below). +# +# Mirrors the fixed-seq-len parallelism options (pure TP and DEP) so the +# agentic sweep can probe both interactivity and throughput regimes: +# pure TP (DP_ATTENTION=false, EP_SIZE=1): attention TP-sharded across +# all $TP GPUs in a single engine. Lower TPOT, lower batch. +# TP+EP (DP_ATTENTION=false, EP_SIZE>1): attention TP-sharded, MoE +# experts EP-sharded within the TP group. +# DEP (DP_ATTENTION=true, EP_SIZE>1): per-DP-rank attention with +# experts EP-sharded across DP ranks (per the vLLM blog recipe). +# Highest aggregate throughput at large CONC. +# +# Image is configured in nvidia-master.yaml. block_size=256, +# kv-cache-dtype=fp8, FLASHINFER_MLA_SPARSE_DSV4 attention with the FP4 indexer +# cache, FULL_DECODE_ONLY cudagraph capture, and (in EP tiers) mega-MoE backend. +# +# Required env vars: +# MODEL, TP, CONC, KV_OFFLOADING, TOTAL_CPU_DRAM_GB, RESULT_DIR +# +# Pure TP is GPU-resident (KV_OFFLOADING=none). DEP tiers offload KV to host +# DRAM: KV_OFFLOADING=dram requires KV_OFFLOAD_BACKEND=vllm-simple or mooncake. + +source "$(dirname "$0")/../../benchmark_lib.sh" + +check_env_vars MODEL TP CONC KV_OFFLOADING TOTAL_CPU_DRAM_GB RESULT_DIR DURATION EP_SIZE DP_ATTENTION + +if [ -z "$DCP_SIZE" ]; then + DCP_SIZE=1 +fi +if [ -z "$PCP_SIZE" ]; then + PCP_SIZE=1 +fi +VLLM_CP_ARGS=() +if [ "$DCP_SIZE" -gt 1 ]; then + VLLM_CP_ARGS+=(--decode-context-parallel-size "$DCP_SIZE") +fi +if [ "$PCP_SIZE" -gt 1 ]; then + VLLM_CP_ARGS+=(--prefill-context-parallel-size "$PCP_SIZE") +fi + +GPU_COUNT=$TP +if [[ ! "$GPU_COUNT" =~ ^[1-9][0-9]*$ ]]; then + echo "Error: GPU_COUNT must be a positive integer, got '$GPU_COUNT'" >&2 + exit 1 +fi +export GPU_COUNT + +if [[ -n "$SLURM_JOB_ID" ]]; then + echo "JOB $SLURM_JOB_ID running on $SLURMD_NODENAME" +fi + +# `hf download` creates the target dir if missing and is itself idempotent. +# When MODEL_PATH is unset (stand-alone runs), fall back to the HF_HUB_CACHE +# Either way, MODEL_PATH is what the server is launched with. +if [[ -n "$MODEL_PATH" ]]; then + if [[ ! -d "$MODEL_PATH" || -z "$(ls -A "$MODEL_PATH" 2>/dev/null)" ]]; then + hf download "$MODEL" --local-dir "$MODEL_PATH" + fi +else + hf download "$MODEL" + export MODEL_PATH="$MODEL" +fi +nvidia-smi + +# ---- Resolve traces and install deps ---------------------------------------- +resolve_trace_source +install_agentic_deps + +# vllm-project/router expands the one HTTP backend into one logical worker per +# DP rank and sends X-data-parallel-rank on forwarded requests. aiperf's +# X-Correlation-ID is stable for every turn of a conversation; alias it to the +# router's preferred X-Session-ID header. +USE_VLLM_ROUTER=false +VLLM_BACKEND_PORT="$PORT" +if [ "$DP_ATTENTION" = "true" ]; then + USE_VLLM_ROUTER=true + VLLM_BACKEND_PORT=$((PORT + 1)) + VLLM_ROUTER_VERSION=0.1.14 + VLLM_ROUTER_POLICY=consistent_hash + VLLM_ROUTER_METRICS_PORT=$((PORT + 10000)) + export AIPERF_HTTP_X_SESSION_ID_FROM_CORRELATION_ID=1 + agentic_pip_install --quiet "vllm-router==$VLLM_ROUTER_VERSION" +fi + +# DeepSeek-V4-Pro weights are large; engine startup can exceed default 600s. +export VLLM_ENGINE_READY_TIMEOUT_S=3600 + +# vllm-project/vllm#43447 keeps local SWA prefix-cache tails sparsely, while +# vllm-project/vllm#44774 applies the same reachability policy to Mooncake's +# store mask. 32k matches the trace-replay tuning validated for this workload. +export VLLM_PREFIX_CACHE_RETENTION_INTERVAL=32768 +export VLLM_USE_V2_MODEL_RUNNER=1 +export VLLM_USE_RUST_FRONTEND=1 +export VLLM_DSV4_MEGA_FP8_COMBINE=1 +export VLLM_RPC_TIMEOUT=600000 + +# ---- Server config ---------------------------------------------------------- +SERVER_LOG="$RESULT_DIR/server.log" +ROUTER_LOG="$RESULT_DIR/router.log" +MOONCAKE_MASTER_LOG="$RESULT_DIR/mooncake_master.log" +mkdir -p "$RESULT_DIR" + +SERVER_PID="" +ROUTER_PID="" +MOONCAKE_MASTER_PID="" + +OFFLOAD_ARGS=() +case "$KV_OFFLOAD_BACKEND" in + "") + require_agentic_kv_offload_none + ;; + vllm-simple) + require_agentic_kv_offload_backend vllm-simple + CPU_BYTES_PER_RANK=$(( TOTAL_CPU_DRAM_GB * 1000 * 1000 * 1000 / GPU_COUNT )) + # Identical prefixes must hash to identical block keys across DP ranks. + export PYTHONHASHSEED=42 + OFFLOAD_CONFIG=$(cat </dev/null + + MOONCAKE_MASTER_PORT=$((PORT + 12000)) + MOONCAKE_CONFIG_PATH="$RESULT_DIR/mooncake_config.json" + cat > "$MOONCAKE_CONFIG_PATH" < "$MOONCAKE_MASTER_LOG" 2>&1 & + MOONCAKE_MASTER_PID=$! + sleep 2 + if ! kill -0 "$MOONCAKE_MASTER_PID" 2>/dev/null; then + echo "Mooncake master died during startup." >&2 + cat "$MOONCAKE_MASTER_LOG" >&2 + exit 1 + fi + unset VLLM_USE_SIMPLE_KV_OFFLOAD + OFFLOAD_ARGS=( + --kv-transfer-config + '{"kv_connector":"MooncakeStoreConnector","kv_role":"kv_both","kv_connector_extra_config":{"load_async":true}}' + ) + ;; + *) + echo "Error: unsupported B200 KV_OFFLOAD_BACKEND='$KV_OFFLOAD_BACKEND'" >&2 + exit 1 + ;; +esac + +PARALLEL_ARGS=(--tensor-parallel-size "$TP" --data-parallel-size 1) +if [ "$DP_ATTENTION" = "true" ]; then + PARALLEL_ARGS=(--tensor-parallel-size 1 --data-parallel-size "$TP") +fi + +EP_ARGS=() +FAST_MOE_ARGS=() +if [ "$EP_SIZE" -gt 1 ]; then + EP_ARGS=(--enable-expert-parallel) + FAST_MOE_ARGS=( + --moe-backend deep_gemm_amxf4_mega_moe + --enable-ep-weight-filter + --prefill-schedule-interval 16 + ) +fi + +# AgentX concurrency counts live session trees, not individual requests. +# Subagent fan-out can push instantaneous request concurrency above CONC, so +# leave 2x headroom rather than clipping those bursts at the scheduler. +MAX_NUM_SEQS=$((2 * CONC)) + +# MTP: cudagraph capture sizes are in TOKENS. A uniform decode batch of S seqs +# verifies S*(1+num_speculative_tokens) tokens, so cap capture at +# MAX_NUM_SEQS*(1+N) tokens -- otherwise vLLM's FULL_DECODE_ONLY ladder tops out +# at MAX_NUM_SEQS/(1+N) seqs and the largest decode batches fall back to eager. +NUM_SPEC_TOKENS=3 +TOKENS_PER_SEQ=$((1 + NUM_SPEC_TOKENS)) +MAX_CUDAGRAPH_CAPTURE_SIZE=$((MAX_NUM_SEQS * TOKENS_PER_SEQ)) + +echo "Starting vllm server..." +export TORCH_CUDA_ARCH_LIST="10.0" +export PYTHONNOUSERSITE=1 +export VLLM_FLOAT32_MATMUL_PRECISION=high + +{ set +x; } 2>/dev/null +VLLM_CMD=( + vllm serve "$MODEL_PATH" --served-model-name "$MODEL" + --host 0.0.0.0 + --port "$VLLM_BACKEND_PORT" + --trust-remote-code + --kv-cache-dtype fp8 + --block-size 256 + --max-model-len 1048576 + --gpu-memory-utilization 0.92 + --numa-bind + --enable-cumem-allocator + --no-enable-flashinfer-autotune + --tokenizer-mode deepseek_v4 + --reasoning-parser deepseek_v4 + --attention-config '{"backend":"FLASHINFER_MLA_SPARSE_DSV4","use_prefill_query_quantization":true,"use_fp4_indexer_cache":true}' + --speculative-config "{\"method\": \"mtp\", \"num_speculative_tokens\": $NUM_SPEC_TOKENS}" + --no-disable-hybrid-kv-cache-manager + --disable-uvicorn-access-log + --compilation-config '{"cudagraph_mode":"FULL_DECODE_ONLY","mode":0}' + --max-num-seqs "$MAX_NUM_SEQS" + --max-cudagraph-capture-size "$MAX_CUDAGRAPH_CAPTURE_SIZE" + "${PARALLEL_ARGS[@]}" + "${VLLM_CP_ARGS[@]}" + "${EP_ARGS[@]}" + "${FAST_MOE_ARGS[@]}" + "${OFFLOAD_ARGS[@]}" +) +printf '%q ' "${VLLM_CMD[@]}" | tee "$RESULT_DIR/vllm_command.txt" +printf '\n' | tee -a "$RESULT_DIR/vllm_command.txt" +"${VLLM_CMD[@]}" > "$SERVER_LOG" 2>&1 & +SERVER_PID=$! +echo "Server PID: $SERVER_PID" + +wait_for_server_ready --port "$VLLM_BACKEND_PORT" --server-log "$SERVER_LOG" --server-pid "$SERVER_PID" + +if [ "$USE_VLLM_ROUTER" = "true" ]; then + echo "Starting native vLLM router on port $PORT for $TP DP ranks..." + vllm-router \ + --worker-urls "http://localhost:$VLLM_BACKEND_PORT" \ + --policy "$VLLM_ROUTER_POLICY" \ + --intra-node-data-parallel-size "$TP" \ + --host 0.0.0.0 \ + --port "$PORT" \ + --prometheus-host 127.0.0.1 \ + --prometheus-port "$VLLM_ROUTER_METRICS_PORT" \ + --request-timeout-secs 14400 \ + --disable-retries > "$ROUTER_LOG" 2>&1 & + ROUTER_PID=$! + echo "Router PID: $ROUTER_PID" + wait_for_server_ready --port "$PORT" --server-log "$ROUTER_LOG" --server-pid "$ROUTER_PID" +fi + +if [ "${EVAL_ONLY}" = "true" ]; then + run_eval --port "$PORT" +else + build_replay_cmd "$RESULT_DIR" + run_agentic_replay_and_write_outputs "$RESULT_DIR" +fi diff --git a/configs/nvidia-master.yaml b/configs/nvidia-master.yaml index ff922946c5..268bb86721 100644 --- a/configs/nvidia-master.yaml +++ b/configs/nvidia-master.yaml @@ -1784,10 +1784,18 @@ dsv4-fp4-b200-vllm-agentic: search-space: # Pure TP at low concurrency. - { tp: 8, kv-offloading: none, conc-list: [1, 2, 4, 6, 8] } + # Pure TP GPU-resident + MTP (num_speculative_tokens=3) + - { tp: 8, kv-offloading: none, spec-decoding: mtp, conc-list: [1, 2, 4, 6, 8] } - { tp: 8, kv-offloading: dram, kv-offload-backend: { name: vllm-simple, version: "904e4ec" }, conc-list: [8, 12, 16] } + # Pure TP SimpleCPU + MTP (num_speculative_tokens=3) + - { tp: 8, kv-offloading: dram, kv-offload-backend: { name: vllm-simple, version: "904e4ec" }, spec-decoding: mtp, conc-list: [8, 12, 16] } # DEP - { tp: 8, ep: 8, dp-attn: true, kv-offloading: dram, kv-offload-backend: { name: vllm-simple, version: "904e4ec" }, conc-list: [8, 16, 24, 32, 40, 48, 56, 64, 68, 72, 80], router: { name: vllm-router, version: "0.1.14" } } + # DEP SimpleCPU + MTP (num_speculative_tokens=3) + - { tp: 8, ep: 8, dp-attn: true, kv-offloading: dram, kv-offload-backend: { name: vllm-simple, version: "904e4ec" }, spec-decoding: mtp, conc-list: [8, 16, 24, 32, 40, 48, 56, 64, 68, 72, 80], router: { name: vllm-router, version: "0.1.14" } } - { tp: 8, ep: 8, dp-attn: true, kv-offloading: dram, kv-offload-backend: { name: mooncake, version: "0.3.11.post1" }, conc-list: [12, 20, 28, 36, 44, 52, 60, 68, 76], router: { name: vllm-router, version: "0.1.14" } } + # DEP Mooncake + MTP (num_speculative_tokens=3) + - { tp: 8, ep: 8, dp-attn: true, kv-offloading: dram, kv-offload-backend: { name: mooncake, version: "0.3.11.post1" }, spec-decoding: mtp, conc-list: [12, 20, 28, 36, 44, 52, 60, 68, 76], router: { name: vllm-router, version: "0.1.14" } } dsv4-fp4-b200-trt: image: ghcr.io#semianalysisai/trtllm-deepseek-v4:feat-deepseek_v4-c185066 From 83ab93ae5f94c25e7eca51ac3427f72c70b26bc0 Mon Sep 17 00:00:00 2001 From: Jeff Ma Date: Fri, 17 Jul 2026 01:13:16 -0700 Subject: [PATCH 03/11] docs(changelog): add B200 vLLM MTP entry (PR #2259) Document the dsv4-fp4-b200-vllm-agentic MTP twins (num_speculative_tokens=3). Co-Authored-By: Claude Opus 4.8 (1M context) --- perf-changelog.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/perf-changelog.yaml b/perf-changelog.yaml index c620aceaa5..f0c0c93f16 100644 --- a/perf-changelog.yaml +++ b/perf-changelog.yaml @@ -4918,3 +4918,10 @@ - "Image: vllm/vllm-openai:nightly-dev-x86_64-cu13.0.1-904e4ec" - "B300: GPU-resident TP4/TP8 at conc [1,2,4,6,8,12,16,20,24,28,32] and DEP8 at conc [32,64,96,128,160,192,196,224,228] (max-num-batched-tokens 16384, long-prefill-token-threshold 4096, gpu-memory-utilization 0.92); TP4 SimpleCPU lazy-offload at conc [28,32,36,40]; DEP4 at conc [8,16,24,32,40,48,56,64,72] with both SimpleCPU and Mooncake 0.3.11.post1." pr-link: https://github.com/SemiAnalysisAI/InferenceX/pull/2241 + +- config-keys: + - dsv4-fp4-b200-vllm-agentic + description: + - "Add an MTP speculative-decoding twin for every B200 AgentX topology (DeepSeek-V4 built-in MTP): --speculative-config {\"method\":\"mtp\",\"num_speculative_tokens\":3}, routed via spec-decoding=mtp to dsv4_fp4_b200_vllm_mtp.sh -- TP8 GPU-resident, TP8 SimpleCPU, DEP8 SimpleCPU, DEP8 Mooncake, each mirroring its non-MTP conc-list." + - "MTP capture sizes are in TOKENS: --max-cudagraph-capture-size scaled to MAX_NUM_SEQS*(1+num_speculative_tokens) (= MAX_NUM_SEQS*4) so FULL_DECODE_ONLY covers the largest decode batches." + pr-link: https://github.com/SemiAnalysisAI/InferenceX/pull/2259 From 026711b3a4b39faab947986225a073d592050fc8 Mon Sep 17 00:00:00 2001 From: Jeff Ma Date: Fri, 17 Jul 2026 01:28:00 -0700 Subject: [PATCH 04/11] [AgentX] B200 vLLM: split MTP into a separate config-key Restructure so the sweep runs only the new MTP work, not the existing aggregate: - Revert dsv4-fp4-b200-vllm-agentic to its main (#2224) search space -- unchanged vs main, so it is not re-run. - Add dsv4-fp4-b200-vllm-agentic-mtp: MTP twins (num_speculative_tokens=3) of the aggregate arms (TP8 GPU-resident, TP8 SimpleCPU, DEP8 SimpleCPU, DEP8 Mooncake), each mirroring its non-MTP conc-list. - Point the perf-changelog entry at only the new key, so only it sweeps. (No separate TP8 key: B200's TP8 arms already exist in the #2224 aggregate.) Co-Authored-By: Claude Opus 4.8 (1M context) --- configs/nvidia-master.yaml | 21 +++++++++++++++++---- perf-changelog.yaml | 5 +++-- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/configs/nvidia-master.yaml b/configs/nvidia-master.yaml index 268bb86721..2e7a0090b2 100644 --- a/configs/nvidia-master.yaml +++ b/configs/nvidia-master.yaml @@ -1784,16 +1784,29 @@ dsv4-fp4-b200-vllm-agentic: search-space: # Pure TP at low concurrency. - { tp: 8, kv-offloading: none, conc-list: [1, 2, 4, 6, 8] } + - { tp: 8, kv-offloading: dram, kv-offload-backend: { name: vllm-simple, version: "904e4ec" }, conc-list: [8, 12, 16] } + # DEP + - { tp: 8, ep: 8, dp-attn: true, kv-offloading: dram, kv-offload-backend: { name: vllm-simple, version: "904e4ec" }, conc-list: [8, 16, 24, 32, 40, 48, 56, 64, 68, 72, 80], router: { name: vllm-router, version: "0.1.14" } } + - { tp: 8, ep: 8, dp-attn: true, kv-offloading: dram, kv-offload-backend: { name: mooncake, version: "0.3.11.post1" }, conc-list: [12, 20, 28, 36, 44, 52, 60, 68, 76], router: { name: vllm-router, version: "0.1.14" } } + +dsv4-fp4-b200-vllm-agentic-mtp: + image: vllm/vllm-openai:nightly-dev-x86_64-cu13.0.1-904e4ec + model: deepseek-ai/DeepSeek-V4-Pro + model-prefix: dsv4 + runner: cluster:b200-dgxc + precision: fp4 + framework: vllm + multinode: false + scenarios: + agentic-coding: + - dram-utilization: 0.80 + search-space: # Pure TP GPU-resident + MTP (num_speculative_tokens=3) - { tp: 8, kv-offloading: none, spec-decoding: mtp, conc-list: [1, 2, 4, 6, 8] } - - { tp: 8, kv-offloading: dram, kv-offload-backend: { name: vllm-simple, version: "904e4ec" }, conc-list: [8, 12, 16] } # Pure TP SimpleCPU + MTP (num_speculative_tokens=3) - { tp: 8, kv-offloading: dram, kv-offload-backend: { name: vllm-simple, version: "904e4ec" }, spec-decoding: mtp, conc-list: [8, 12, 16] } - # DEP - - { tp: 8, ep: 8, dp-attn: true, kv-offloading: dram, kv-offload-backend: { name: vllm-simple, version: "904e4ec" }, conc-list: [8, 16, 24, 32, 40, 48, 56, 64, 68, 72, 80], router: { name: vllm-router, version: "0.1.14" } } # DEP SimpleCPU + MTP (num_speculative_tokens=3) - { tp: 8, ep: 8, dp-attn: true, kv-offloading: dram, kv-offload-backend: { name: vllm-simple, version: "904e4ec" }, spec-decoding: mtp, conc-list: [8, 16, 24, 32, 40, 48, 56, 64, 68, 72, 80], router: { name: vllm-router, version: "0.1.14" } } - - { tp: 8, ep: 8, dp-attn: true, kv-offloading: dram, kv-offload-backend: { name: mooncake, version: "0.3.11.post1" }, conc-list: [12, 20, 28, 36, 44, 52, 60, 68, 76], router: { name: vllm-router, version: "0.1.14" } } # DEP Mooncake + MTP (num_speculative_tokens=3) - { tp: 8, ep: 8, dp-attn: true, kv-offloading: dram, kv-offload-backend: { name: mooncake, version: "0.3.11.post1" }, spec-decoding: mtp, conc-list: [12, 20, 28, 36, 44, 52, 60, 68, 76], router: { name: vllm-router, version: "0.1.14" } } diff --git a/perf-changelog.yaml b/perf-changelog.yaml index f0c0c93f16..dc58819d1e 100644 --- a/perf-changelog.yaml +++ b/perf-changelog.yaml @@ -4920,8 +4920,9 @@ pr-link: https://github.com/SemiAnalysisAI/InferenceX/pull/2241 - config-keys: - - dsv4-fp4-b200-vllm-agentic + - dsv4-fp4-b200-vllm-agentic-mtp description: - - "Add an MTP speculative-decoding twin for every B200 AgentX topology (DeepSeek-V4 built-in MTP): --speculative-config {\"method\":\"mtp\",\"num_speculative_tokens\":3}, routed via spec-decoding=mtp to dsv4_fp4_b200_vllm_mtp.sh -- TP8 GPU-resident, TP8 SimpleCPU, DEP8 SimpleCPU, DEP8 Mooncake, each mirroring its non-MTP conc-list." + - "Add MTP speculative-decoding for the B200 vLLM AgentX recipe as a separate config-key, so the existing dsv4-fp4-b200-vllm-agentic aggregate is not re-run." + - "dsv4-fp4-b200-vllm-agentic-mtp: MTP twins (--speculative-config {\"method\":\"mtp\",\"num_speculative_tokens\":3}) of the aggregate arms -- TP8 GPU-resident, TP8 SimpleCPU, DEP8 SimpleCPU, DEP8 Mooncake, each mirroring its non-MTP conc-list; routed via spec-decoding=mtp to dsv4_fp4_b200_vllm_mtp.sh." - "MTP capture sizes are in TOKENS: --max-cudagraph-capture-size scaled to MAX_NUM_SEQS*(1+num_speculative_tokens) (= MAX_NUM_SEQS*4) so FULL_DECODE_ONLY covers the largest decode batches." pr-link: https://github.com/SemiAnalysisAI/InferenceX/pull/2259 From bebd5e63d81bf16c9334a0d5d1843a8cb40ac8b6 Mon Sep 17 00:00:00 2001 From: Jeff Ma Date: Fri, 17 Jul 2026 13:41:16 -0700 Subject: [PATCH 05/11] align synthetic acceptance length to 2.49 Use MTP synthetic rejection sampling with acceptance length 2.49, the dsv4-pro golden AL (thinking_on, num_speculative_tokens=3) from golden_al_distribution/dsv4_mtp.yaml. Co-Authored-By: Claude Opus 4.8 (1M context) --- benchmarks/single_node/agentic/dsv4_fp4_b200_vllm_mtp.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/benchmarks/single_node/agentic/dsv4_fp4_b200_vllm_mtp.sh b/benchmarks/single_node/agentic/dsv4_fp4_b200_vllm_mtp.sh index 391a047a71..e645672766 100755 --- a/benchmarks/single_node/agentic/dsv4_fp4_b200_vllm_mtp.sh +++ b/benchmarks/single_node/agentic/dsv4_fp4_b200_vllm_mtp.sh @@ -3,11 +3,11 @@ set -eo pipefail set -x # Agentic trace replay benchmark for DeepSeek-V4-Pro FP4 on B200 using vLLM, -# with MTP speculative decoding (num_speculative_tokens=3). +# with MTP speculative decoding (num_speculative_tokens=3, synthetic acceptance length 2.49). # # Identical to dsv4_fp4_b200_vllm.sh (same image, engine args, offload, GPU # topologies, and agentic aiperf rig) with exactly two MTP deltas: -# --speculative-config '{"method": "mtp", "num_speculative_tokens": 3}' +# --speculative-config '{"method": "mtp", "num_speculative_tokens": 3, "rejection_sample_method": "synthetic", "synthetic_acceptance_length": 2.49}' # --max-cudagraph-capture-size expressed in TOKENS (see the capture block below). # # Mirrors the fixed-seq-len parallelism options (pure TP and DEP) so the @@ -233,6 +233,9 @@ MAX_NUM_SEQS=$((2 * CONC)) # MAX_NUM_SEQS*(1+N) tokens -- otherwise vLLM's FULL_DECODE_ONLY ladder tops out # at MAX_NUM_SEQS/(1+N) seqs and the largest decode batches fall back to eager. NUM_SPEC_TOKENS=3 +# Standardize MTP acceptance to the dsv4-pro golden AL (thinking_on, +# num_speculative_tokens=3) from golden_al_distribution/dsv4_mtp.yaml. +SYNTHETIC_ACCEPT_LEN=2.49 TOKENS_PER_SEQ=$((1 + NUM_SPEC_TOKENS)) MAX_CUDAGRAPH_CAPTURE_SIZE=$((MAX_NUM_SEQS * TOKENS_PER_SEQ)) @@ -257,7 +260,7 @@ VLLM_CMD=( --tokenizer-mode deepseek_v4 --reasoning-parser deepseek_v4 --attention-config '{"backend":"FLASHINFER_MLA_SPARSE_DSV4","use_prefill_query_quantization":true,"use_fp4_indexer_cache":true}' - --speculative-config "{\"method\": \"mtp\", \"num_speculative_tokens\": $NUM_SPEC_TOKENS}" + --speculative-config "{\"method\": \"mtp\", \"num_speculative_tokens\": $NUM_SPEC_TOKENS, \"rejection_sample_method\": \"synthetic\", \"synthetic_acceptance_length\": $SYNTHETIC_ACCEPT_LEN}" --no-disable-hybrid-kv-cache-manager --disable-uvicorn-access-log --compilation-config '{"cudagraph_mode":"FULL_DECODE_ONLY","mode":0}' From 5187947e21cce9c8260af77b2e804fc701b15aa8 Mon Sep 17 00:00:00 2001 From: Jeff Ma Date: Fri, 17 Jul 2026 14:44:34 -0700 Subject: [PATCH 06/11] reduce GPU memory utilization from 0.92 to 0.9 Lower gpu-memory-utilization for the B200 vLLM MTP recipe to 0.9 for extra headroom. Co-Authored-By: Claude Opus 4.8 (1M context) --- benchmarks/single_node/agentic/dsv4_fp4_b200_vllm_mtp.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/single_node/agentic/dsv4_fp4_b200_vllm_mtp.sh b/benchmarks/single_node/agentic/dsv4_fp4_b200_vllm_mtp.sh index e645672766..93a3a22cd5 100755 --- a/benchmarks/single_node/agentic/dsv4_fp4_b200_vllm_mtp.sh +++ b/benchmarks/single_node/agentic/dsv4_fp4_b200_vllm_mtp.sh @@ -253,7 +253,7 @@ VLLM_CMD=( --kv-cache-dtype fp8 --block-size 256 --max-model-len 1048576 - --gpu-memory-utilization 0.92 + --gpu-memory-utilization 0.9 --numa-bind --enable-cumem-allocator --no-enable-flashinfer-autotune From a32d07f833d673508770bab5ff26c68fef55290e Mon Sep 17 00:00:00 2001 From: Jeff Ma Date: Sat, 18 Jul 2026 02:22:20 -0700 Subject: [PATCH 07/11] Revert "fix(changelog): accept single-node agentic eval rows in matrix validation" This reverts commit 6efd246c81a3f129b16bb6d665bca1dc0e666fcd. --- utils/matrix_logic/validation.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/utils/matrix_logic/validation.py b/utils/matrix_logic/validation.py index 7403c795e4..6b7c671289 100644 --- a/utils/matrix_logic/validation.py +++ b/utils/matrix_logic/validation.py @@ -291,10 +291,6 @@ class SingleNodeAgenticMatrixEntry(BaseModel): duration: int = Field(alias=Fields.DURATION.value) exp_name: str = Field(alias=Fields.EXP_NAME.value) scenario_type: str = Field(alias=Fields.SCENARIO_TYPE.value) - # Agentic eval rows (SWE-bench) carry run_eval/eval_only; benchmark rows omit - # them. Optional so both shapes validate and benchmark output is unchanged. - run_eval: Optional[bool] = Field(default=None, alias=Fields.RUN_EVAL.value) - eval_only: Optional[bool] = Field(default=None, alias=Fields.EVAL_ONLY.value) @model_validator(mode='after') def validate_kv_offload_fields(self): @@ -893,7 +889,7 @@ class ChangelogMatrixEntry(BaseModel): ] = Field(default_factory=dict) multi_node: dict[str, list[Union[MultiNodeMatrixEntry, MultiNodeAgenticMatrixEntry]] ] = Field(default_factory=dict) - evals: list[Union[SingleNodeMatrixEntry, SingleNodeAgenticMatrixEntry]] = Field(default_factory=list) + evals: list[SingleNodeMatrixEntry] = Field(default_factory=list) multinode_evals: list[MultiNodeMatrixEntry] = Field(default_factory=list) changelog_metadata: ChangelogMetadata From 425fd8656656ae137a7745f966a304f8470a39aa Mon Sep 17 00:00:00 2001 From: Jeff Ma Date: Sat, 18 Jul 2026 14:16:05 -0700 Subject: [PATCH 08/11] MTP synthetic acceptance for throughput, real verification for eval Make the B200 MTP --speculative-config conditional on EVAL_ONLY: throughput points pin synthetic acceptance length 2.49 (dsv4-pro golden AL), while the auto-selected SWE-bench accuracy run (EVAL_ONLY=true) uses real MTP with normal target verification. Synthetic acceptance bypasses verification and corrupts the eval output (0.0000 score). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../agentic/dsv4_fp4_b200_vllm_mtp.sh | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/benchmarks/single_node/agentic/dsv4_fp4_b200_vllm_mtp.sh b/benchmarks/single_node/agentic/dsv4_fp4_b200_vllm_mtp.sh index 93a3a22cd5..0ee50e029f 100755 --- a/benchmarks/single_node/agentic/dsv4_fp4_b200_vllm_mtp.sh +++ b/benchmarks/single_node/agentic/dsv4_fp4_b200_vllm_mtp.sh @@ -3,11 +3,12 @@ set -eo pipefail set -x # Agentic trace replay benchmark for DeepSeek-V4-Pro FP4 on B200 using vLLM, -# with MTP speculative decoding (num_speculative_tokens=3, synthetic acceptance length 2.49). +# with MTP speculative decoding (num_speculative_tokens=3): synthetic acceptance +# length 2.49 for throughput, real target verification for the EVAL_ONLY eval. # # Identical to dsv4_fp4_b200_vllm.sh (same image, engine args, offload, GPU # topologies, and agentic aiperf rig) with exactly two MTP deltas: -# --speculative-config '{"method": "mtp", "num_speculative_tokens": 3, "rejection_sample_method": "synthetic", "synthetic_acceptance_length": 2.49}' +# --speculative-config: synthetic acceptance length 2.49 (throughput) vs real MTP (EVAL_ONLY); see the SPEC_CONFIG block # --max-cudagraph-capture-size expressed in TOKENS (see the capture block below). # # Mirrors the fixed-seq-len parallelism options (pure TP and DEP) so the @@ -233,9 +234,16 @@ MAX_NUM_SEQS=$((2 * CONC)) # MAX_NUM_SEQS*(1+N) tokens -- otherwise vLLM's FULL_DECODE_ONLY ladder tops out # at MAX_NUM_SEQS/(1+N) seqs and the largest decode batches fall back to eager. NUM_SPEC_TOKENS=3 -# Standardize MTP acceptance to the dsv4-pro golden AL (thinking_on, -# num_speculative_tokens=3) from golden_al_distribution/dsv4_mtp.yaml. +# Throughput pins synthetic MTP acceptance to the dsv4-pro golden AL (thinking_on, +# num_speculative_tokens=3, golden_al_distribution/dsv4_mtp.yaml). The EVAL_ONLY +# accuracy run uses real target verification instead -- synthetic acceptance +# bypasses verification and corrupts the SWE-bench eval (0.0000 score). SYNTHETIC_ACCEPT_LEN=2.49 +if [ "${EVAL_ONLY:-false}" = "true" ]; then + SPEC_CONFIG="{\"method\": \"mtp\", \"num_speculative_tokens\": $NUM_SPEC_TOKENS}" +else + SPEC_CONFIG="{\"method\": \"mtp\", \"num_speculative_tokens\": $NUM_SPEC_TOKENS, \"rejection_sample_method\": \"synthetic\", \"synthetic_acceptance_length\": $SYNTHETIC_ACCEPT_LEN}" +fi TOKENS_PER_SEQ=$((1 + NUM_SPEC_TOKENS)) MAX_CUDAGRAPH_CAPTURE_SIZE=$((MAX_NUM_SEQS * TOKENS_PER_SEQ)) @@ -260,7 +268,7 @@ VLLM_CMD=( --tokenizer-mode deepseek_v4 --reasoning-parser deepseek_v4 --attention-config '{"backend":"FLASHINFER_MLA_SPARSE_DSV4","use_prefill_query_quantization":true,"use_fp4_indexer_cache":true}' - --speculative-config "{\"method\": \"mtp\", \"num_speculative_tokens\": $NUM_SPEC_TOKENS, \"rejection_sample_method\": \"synthetic\", \"synthetic_acceptance_length\": $SYNTHETIC_ACCEPT_LEN}" + --speculative-config "$SPEC_CONFIG" --no-disable-hybrid-kv-cache-manager --disable-uvicorn-access-log --compilation-config '{"cudagraph_mode":"FULL_DECODE_ONLY","mode":0}' From 4b5cb056f8117e29586b1dd188bbb14803964eec Mon Sep 17 00:00:00 2001 From: Jeff Ma Date: Sun, 19 Jul 2026 10:13:59 -0700 Subject: [PATCH 09/11] add missing tool call parser for swe eval Signed-off-by: Jeff Ma --- benchmarks/single_node/agentic/dsv4_fp4_b200_vllm_mtp.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/benchmarks/single_node/agentic/dsv4_fp4_b200_vllm_mtp.sh b/benchmarks/single_node/agentic/dsv4_fp4_b200_vllm_mtp.sh index 0ee50e029f..d36bc99a5a 100755 --- a/benchmarks/single_node/agentic/dsv4_fp4_b200_vllm_mtp.sh +++ b/benchmarks/single_node/agentic/dsv4_fp4_b200_vllm_mtp.sh @@ -266,6 +266,8 @@ VLLM_CMD=( --enable-cumem-allocator --no-enable-flashinfer-autotune --tokenizer-mode deepseek_v4 + --tool-call-parser deepseek_v4 + --enable-auto-tool-choice --reasoning-parser deepseek_v4 --attention-config '{"backend":"FLASHINFER_MLA_SPARSE_DSV4","use_prefill_query_quantization":true,"use_fp4_indexer_cache":true}' --speculative-config "$SPEC_CONFIG" From fc1c58487d76159f15ca8e29fdb3752f0761613a Mon Sep 17 00:00:00 2001 From: Cam Quilici Date: Mon, 20 Jul 2026 15:31:36 -0500 Subject: [PATCH 10/11] fix(agentic): preserve B200 MTP PCP GPU count --- benchmarks/single_node/agentic/dsv4_fp4_b200_vllm_mtp.sh | 2 +- configs/nvidia-master.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/benchmarks/single_node/agentic/dsv4_fp4_b200_vllm_mtp.sh b/benchmarks/single_node/agentic/dsv4_fp4_b200_vllm_mtp.sh index d36bc99a5a..44a8cf7bf7 100755 --- a/benchmarks/single_node/agentic/dsv4_fp4_b200_vllm_mtp.sh +++ b/benchmarks/single_node/agentic/dsv4_fp4_b200_vllm_mtp.sh @@ -49,7 +49,7 @@ if [ "$PCP_SIZE" -gt 1 ]; then VLLM_CP_ARGS+=(--prefill-context-parallel-size "$PCP_SIZE") fi -GPU_COUNT=$TP +GPU_COUNT="${GPU_COUNT:-$((TP * PCP_SIZE))}" if [[ ! "$GPU_COUNT" =~ ^[1-9][0-9]*$ ]]; then echo "Error: GPU_COUNT must be a positive integer, got '$GPU_COUNT'" >&2 exit 1 diff --git a/configs/nvidia-master.yaml b/configs/nvidia-master.yaml index 37e7d0e9a6..a6d9a5872d 100644 --- a/configs/nvidia-master.yaml +++ b/configs/nvidia-master.yaml @@ -979,9 +979,9 @@ dsv4-fp4-b200-vllm-agentic-mtp: # Pure TP GPU-resident + MTP (num_speculative_tokens=3) - { tp: 8, kv-offloading: none, spec-decoding: mtp, conc-list: [1, 2, 4, 6, 8] } # Pure TP SimpleCPU + MTP (num_speculative_tokens=3) - - { tp: 8, kv-offloading: dram, kv-offload-backend: { name: vllm-simple, version: "904e4ec" }, spec-decoding: mtp, conc-list: [8, 12, 16] } + - { tp: 8, kv-offloading: dram, kv-offload-backend: { name: vllm-simple }, spec-decoding: mtp, conc-list: [8, 12, 16] } # DEP SimpleCPU + MTP (num_speculative_tokens=3) - - { tp: 8, ep: 8, dp-attn: true, kv-offloading: dram, kv-offload-backend: { name: vllm-simple, version: "904e4ec" }, spec-decoding: mtp, conc-list: [8, 16, 24, 32, 40, 48, 56, 64, 68, 72, 80], router: { name: vllm-router, version: "0.1.14" } } + - { tp: 8, ep: 8, dp-attn: true, kv-offloading: dram, kv-offload-backend: { name: vllm-simple }, spec-decoding: mtp, conc-list: [8, 16, 24, 32, 40, 48, 56, 64, 68, 72, 80], router: { name: vllm-router, version: "0.1.14" } } # DEP Mooncake + MTP (num_speculative_tokens=3) - { tp: 8, ep: 8, dp-attn: true, kv-offloading: dram, kv-offload-backend: { name: mooncake, version: "0.3.11.post1" }, spec-decoding: mtp, conc-list: [12, 20, 28, 36, 44, 52, 60, 68, 76], router: { name: vllm-router, version: "0.1.14" } } From 579c16f00a131c11fe68473fa4e87c99c07effb7 Mon Sep 17 00:00:00 2001 From: Cam Quilici Date: Mon, 20 Jul 2026 15:34:48 -0500 Subject: [PATCH 11/11] fix(agentic): derive B200 MTP GPU count from topology --- benchmarks/single_node/agentic/dsv4_fp4_b200_vllm_mtp.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/single_node/agentic/dsv4_fp4_b200_vllm_mtp.sh b/benchmarks/single_node/agentic/dsv4_fp4_b200_vllm_mtp.sh index 44a8cf7bf7..85ffd64609 100755 --- a/benchmarks/single_node/agentic/dsv4_fp4_b200_vllm_mtp.sh +++ b/benchmarks/single_node/agentic/dsv4_fp4_b200_vllm_mtp.sh @@ -49,7 +49,7 @@ if [ "$PCP_SIZE" -gt 1 ]; then VLLM_CP_ARGS+=(--prefill-context-parallel-size "$PCP_SIZE") fi -GPU_COUNT="${GPU_COUNT:-$((TP * PCP_SIZE))}" +GPU_COUNT=$((TP * PCP_SIZE)) if [[ ! "$GPU_COUNT" =~ ^[1-9][0-9]*$ ]]; then echo "Error: GPU_COUNT must be a positive integer, got '$GPU_COUNT'" >&2 exit 1