Skip to content
Merged
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
8 changes: 8 additions & 0 deletions benchmarks/benchmark_lib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,14 @@ append_command() {
printf '\n' >> "$output_file"
}

# Persist an argv array in shell-replayable form.
write_command() {
local output_file="$1"
shift
printf '%q ' "$@" | tee "$output_file"
printf '\n' | tee -a "$output_file"
}

# Run benchmark serving with standardized parameters
# All parameters are required except --endpoint, --use-chat-template, --dsv4, and --trust-remote-code
# Parameters:
Expand Down
151 changes: 151 additions & 0 deletions benchmarks/single_node/agentic/qwen3.5_fp8_mi325x_mtp.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
#!/usr/bin/env bash
set -eo pipefail
set -x

# AgentX trace replay for Qwen3.5-397B-A17B FP8 on MI325X with SGLang
# native EAGLE/NEXTN MTP. Throughput uses the committed golden synthetic
# acceptance length; evaluation retains real target-model verification.

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

export EVAL_FRAMEWORK="lm-eval"

check_env_vars \
MODEL TP CONC EP_SIZE \
TOTAL_CPU_DRAM_GB RESULT_DIR DURATION

SCHEDULER_RECV_INTERVAL=${SCHEDULER_RECV_INTERVAL:-30}

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

if [[ -n "${ROCR_VISIBLE_DEVICES:-}" ]]; then
export HIP_VISIBLE_DEVICES="$ROCR_VISIBLE_DEVICES"
fi

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

rocm-smi || true
amd-smi || true

export WEKA_LOADER_OVERRIDE=semianalysis_cc_traces_weka_062126_256k
resolve_trace_source
install_agentic_deps

# This is a single aggregate SGLang engine, so one logical backend metrics
# endpoint is authoritative. build_replay_cmd also discovers the public
# endpoint; AIPerf deduplicates the explicit copy.
export AIPERF_SERVER_METRICS_URLS="http://localhost:${PORT}/metrics"
export AIPERF_REQUIRED_SERVER_METRIC_PREFIX="sglang:"

SERVER_LOG="$RESULT_DIR/server.log"
mkdir -p "$RESULT_DIR"

SERVER_PID=""
cleanup_agentic_services() {
local exit_code=$?
trap - EXIT INT TERM
set +e
stop_background_process_tree "$SERVER_PID" "SGLang server" 60
exit "$exit_code"
}
trap cleanup_agentic_services EXIT
trap 'exit 130' INT
trap 'exit 143' TERM

PARALLEL_ARGS=(
--tp "$TP"
--dp 1
--ep-size "$EP_SIZE"
)

TOKENIZER_ARGS=()
if [ "$TP" -ge 4 ]; then
TOKENIZER_ARGS=(--tokenizer-worker-num 6)
fi

# AgentX concurrency counts live session trees rather than HTTP requests.
# Keep capacity for subagent fan-out while bounding graph-capture memory.
MAX_RUNNING_REQUESTS=$((2 * CONC))
CUDA_GRAPH_MAX_BS="$CONC"
[ "$CUDA_GRAPH_MAX_BS" -gt 64 ] && CUDA_GRAPH_MAX_BS=64

# With EAGLE enabled, SGLang reserves 15% of this budget for the draft model.
# TP2 needs an effective 0.8075 target-model budget to fit Qwen3.5's weights,
# hybrid state, and KV pools; TP4/TP8 retain the established 0.68 budget.
MEM_FRACTION_STATIC=0.80
if [ "$TP" -eq 2 ]; then
MEM_FRACTION_STATIC=0.95
fi

export PYTHONNOUSERSITE=1
export AIPERF_HTTP_TCP_USER_TIMEOUT=900000
export SGLANG_USE_AITER=1
export SGLANG_USE_AITER_UNIFIED_ATTN=1
export SGLANG_TIMEOUT_KEEP_ALIVE=1800

# Synthetic rejection sampling is only for performance replay. The AL is the
# committed Qwen3.5 thinking-on value for three speculative tokens. Evals use
# real target-model verification.
if [ "${EVAL_ONLY:-false}" != "true" ]; then
export SGLANG_SIMULATE_ACC_LEN=3.39
export SGLANG_SIMULATE_ACC_METHOD=match-expected
export SGLANG_SIMULATE_ACC_TOKEN_MODE=real-draft-token
fi

SGLANG_CMD=(
python3 -m sglang.launch_server
--model-path "$MODEL_PATH"
--served-model-name "$MODEL"
--host 0.0.0.0
--port "$PORT"
--trust-remote-code
"${PARALLEL_ARGS[@]}"
--attention-backend aiter
--quantization fp8
--kv-cache-dtype fp8_e4m3
--mamba-ssm-dtype bfloat16
--mem-fraction-static "$MEM_FRACTION_STATIC"
--model-loader-extra-config '{"enable_multithread_load": true}'
--watchdog-timeout 1200
--enable-aiter-allreduce-fusion
--page-size 16
--cuda-graph-max-bs "$CUDA_GRAPH_MAX_BS"
--max-running-requests "$MAX_RUNNING_REQUESTS"
--max-prefill-tokens 32768
--chunked-prefill-size 32768
--scheduler-recv-interval "$SCHEDULER_RECV_INTERVAL"
--stream-interval 50
"${TOKENIZER_ARGS[@]}"
--tokenizer-path "$MODEL"
--reasoning-parser qwen3
--tool-call-parser qwen3_coder
--speculative-algorithm EAGLE
--speculative-num-steps 3
--speculative-eagle-topk 1
--speculative-num-draft-tokens 4
--enable-metrics
--enable-cache-report
)

write_command "$RESULT_DIR/sglang_command.txt" "${SGLANG_CMD[@]}"
"${SGLANG_CMD[@]}" > "$SERVER_LOG" 2>&1 &
SERVER_PID=$!

wait_for_server_ready --port "$PORT" --server-log "$SERVER_LOG" --server-pid "$SERVER_PID"

if [ "${EVAL_ONLY:-false}" = "true" ]; then
run_eval --port "$PORT"
else
build_replay_cmd "$RESULT_DIR"
REPLAY_CMD+=" --apply-chat-template"
run_agentic_replay_and_write_outputs "$RESULT_DIR"
fi
Comment on lines +146 to +151

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 The script appends --use-chat-template to REPLAY_CMD (lines 145-147), but that flag only exists on run_benchmark_serving's fixed-seq-len path, not on the aiperf CLI that build_replay_cmd invokes here. build_replay_cmd already sets --endpoint /v1/chat/completions and --endpoint-type chat, so aiperf will reject the unrecognized flag and fail the replay step for every throughput concurrency arm (62 points) in this recipe. Fix by deleting the REPLAY_CMD+=" --use-chat-template" line.

Extended reasoning...

What the bug is

benchmark_lib.sh's build_replay_cmd constructs REPLAY_CMD as an invocation of $AIPERF_CLI profile --scenario inferencex-agentx-mvp ... (benchmark_lib.sh:1927), where AIPERF_CLI="${AIPERF_VENV}/bin/aiperf" (benchmark_lib.sh:1721) — the external NVIDIA aiperf Python CLI. This is a completely different program from run_benchmark_serving, and --use-chat-template is not one of its recognized arguments.

--use-chat-template is defined only inside run_benchmark_serving's own bash arg-parsing loop (benchmark_lib.sh:520-526), which is consumed by the fixed-seq-len benchmarking path and appended to benchmark_cmd (not REPLAY_CMD) at benchmark_lib.sh:636-638. It has no meaning to aiperf profile.

The code path that triggers it

In qwen3.5_fp8_mi325x_mtp.sh (lines 144-149):

build_replay_cmd "$RESULT_DIR"
REPLAY_CMD+=" --use-chat-template"
run_agentic_replay_and_write_outputs "$RESULT_DIR"

build_replay_cmd already sets --endpoint /v1/chat/completions and --endpoint-type chat (benchmark_lib.sh:1929-1930), which is aiperf's own mechanism for chat-formatted prompts — nothing further is needed. The extra line simply appends a bogus flag to the string that later gets executed as $REPLAY_CMD inside run_agentic_replay_and_write_outputs.

Why nothing prevents it

There's no static validation of REPLAY_CMD before it's executed — it's a plain bash string built up with += and later eval'd/executed directly. aiperf's own argparse-based CLI is what will reject the unknown flag, but only at runtime, once the sweep is actually dispatched to a runner.

Impact

Every non-EVAL_ONLY throughput arm in this recipe (the full 62-point discovery grid across TP2/EP2, TP4, TEP4, TP8, TEP8) takes the else branch that calls build_replay_cmd + this bad append + run_agentic_replay_and_write_outputs. aiperf will exit non-zero on the unrecognized argument, so run_agentic_replay_and_write_outputs will fail for every one of those 62 points. Only EVAL_ONLY=true runs (which take the run_eval branch instead) are unaffected.

Root cause

This is a misapplication of the AGENTS.md rule "Every *_mtp.sh passes --use-chat-template to run_benchmark_serving." That rule is scoped specifically to run_benchmark_serving (the fixed-seq-len benchmarking path). This script is an agentic recipe that never calls run_benchmark_serving — it drives aiperf directly via build_replay_cmd/run_agentic_replay_and_write_outputs. A repo-wide check confirms no other agentic *_mtp.sh script (e.g. qwen3.5_fp8_b300_sglang_mtp.sh, glm5.2_fp4_mi355x_sglang_mtp.sh, dsv4_fp4_mi355x_vllm_mtp.sh) appends --use-chat-template to REPLAY_CMD; they only append legitimate aiperf flags like --server-metrics. In fact, the sibling kimik3_fp4_b300_vllm_mtp.sh carries an explicit comment noting this exact distinction and stating there's "Nothing to add here" for agentic recipes.

Step-by-step proof

  1. Script reaches the else branch (non-EVAL_ONLY) at line 143.
  2. build_replay_cmd "$RESULT_DIR" sets REPLAY_CMD="$AIPERF_VENV/bin/aiperf profile --scenario inferencex-agentx-mvp --url http://localhost:$PORT --endpoint /v1/chat/completions --endpoint-type chat --streaming --model $MODEL --concurrency $CONC --benchmark-duration $duration --stats-interval 30 ...".
  3. Line 147 appends: REPLAY_CMD+=" --use-chat-template".
  4. run_agentic_replay_and_write_outputs "$RESULT_DIR" executes $REPLAY_CMD, i.e. runs aiperf profile ... --use-chat-template.
  5. aiperf's argparse-based CLI does not recognize --use-chat-template and exits non-zero, causing run_agentic_replay_and_write_outputs to report a failed replay for that concurrency arm.
  6. This repeats identically for all 62 concurrency points in the qwen3.5-fp8-mi325x-sglang-agentic-mtp grid.

Fix

Simply delete line 147 (REPLAY_CMD+=" --use-chat-template"); build_replay_cmd's existing --endpoint-type chat already covers chat formatting for the aiperf replay.

21 changes: 21 additions & 0 deletions configs/amd-master.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,27 @@ qwen3.5-fp8-mi355x-sglang-agentic:
- search-space:
- { tp: 8, ep: 1, kv-offloading: none, conc-list: [1, 2, 4, 8, 16, 32] }

# MI325X official matrix selected from the complete 62-point fast sweep. TP2
# peaks at c4, TP4/TEP4 at c40, and TP8/TEP8 at c64; the next point beyond each
# knee is retained to document the throughput cliff.
qwen3.5-fp8-mi325x-sglang-agentic-mtp:
image: lmsysorg/sglang:v0.5.16-rocm720-mi30x
model: Qwen/Qwen3.5-397B-A17B-FP8
model-prefix: qwen3.5
runner: cluster:mi325x-amds
precision: fp8
framework: sglang
multinode: false
scenarios:
agentic-coding:
- dram-utilization: 0.80
search-space:
- { tp: 2, ep: 2, spec-decoding: mtp, kv-offloading: none, conc-list: [1, 4, 6] }
- { tp: 4, ep: 1, spec-decoding: mtp, kv-offloading: none, conc-list: [1, 8, 16, 32, 40, 48] }
- { tp: 4, ep: 4, spec-decoding: mtp, kv-offloading: none, conc-list: [4, 16, 32, 40, 48] }
- { tp: 8, ep: 1, spec-decoding: mtp, kv-offloading: none, conc-list: [1, 4, 8, 12, 16, 24, 32, 40, 48, 64, 80] }
- { tp: 8, ep: 8, spec-decoding: mtp, kv-offloading: none, conc-list: [4, 16, 24, 32, 40, 48, 64] }

qwen3.5-fp8-mi355x-atom:
image: rocm/atom:rocm7.2.3_ubuntu24.04_py3.12_pytorch_release_2.10.0_atom20260511
model: Qwen/Qwen3.5-397B-A17B-FP8
Expand Down
10 changes: 10 additions & 0 deletions perf-changelog.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5856,3 +5856,13 @@
- "Give the released SGLang router one hour to register the 28-minute-loading backend and route AgentX sessions by correlation ID."
- "Bound hybrid-state pools to the measured resident and HiCache operating ranges instead of reserving capacity that cannot fit beside target and MTP weights."
pr-link: https://github.com/SemiAnalysisAI/InferenceX/pull/2568

- config-keys:
- qwen3.5-fp8-mi325x-sglang-agentic-mtp
scenario-type:
- agentic-coding
description:
- "Add MI325X Qwen3.5 FP8 AgentX with SGLang native EAGLE MTP and golden synthetic acceptance length 3.39"
- "Use lmsysorg/sglang:v0.5.16-rocm720-mi30x, FP8 KV cache, AITER attention, and required SGLang Prometheus metrics"
- "Select a 32-point official TP2/EP2, TP4, TEP4, TP8, and TEP8 matrix from the complete 62-point fast discovery sweep"
pr-link: https://github.com/SemiAnalysisAI/InferenceX/pull/2566