Skip to content

Commit 13f55eb

Browse files
authored
[DSv4][bring-up] FP8 MI325X SGLang benchmark / DSv4 FP8 MI325X SGLang (#2195)
1 parent 4bdc125 commit 13f55eb

5 files changed

Lines changed: 354 additions & 0 deletions

File tree

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
#!/usr/bin/env bash
2+
set -eo pipefail
3+
4+
# DeepSeek-V4-Pro FP8 single-node on MI325X (gfx942) via vLLM.
5+
#
6+
# EXTRAPOLATED bring-up recipe. The sglang path was abandoned: on gfx942
7+
# (no native FP4) the dsv4 sglang backend's nvfp4 MoE / TileLang-MLA kernels
8+
# have no gfx942 equivalents (they exist only for gfx950/MI355X). vLLM instead
9+
# runs the checkpoint in FP8 via --quantization deepseek_v4_fp8, which
10+
# dequantizes the FP4 MoE experts to FP8 — the same path the H200 dsv4 vLLM
11+
# recipe uses (H200 is also a no-FP4 SKU). Derived from:
12+
# * same model + framework + AMD family: dsv4_fp4_mi355x_vllm.sh (ROCm vLLM
13+
# dsv4 structure: AITER MoE, deepseek_v4 tokenizer/parser, mp executor,
14+
# FULL_AND_PIECEWISE compile)
15+
# * same model, FP8 path: dsv4_fp8_h200.sh (--quantization deepseek_v4_fp8)
16+
# * same SKU, different model: minimaxm3_fp8_mi325x.sh (gfx942 vLLM/AITER)
17+
#
18+
# The FP4->FP8 dequant roughly doubles the MoE footprint (~1.05 TB total),
19+
# which fits 8x256 GB comfortably at TP8, so the sweep is TP8-only.
20+
#
21+
# MoE backend is left at auto (NOT --moe-backend aiter) — see dsv4_fp8_mi300x.sh:
22+
# on gfx942, forcing aiter selects AITER_MXFP4_MXFP4 (W4A4 native-mxfp4) which
23+
# the gfx942 kernel rejects; auto's ROCm+DeepseekV4 path prefers
24+
# AITER_MXFP4_BF16 (W4A16, dequant) with a TRITON_UNFUSED fallback.
25+
26+
source "$(dirname "$0")/../../benchmark_lib.sh"
27+
28+
check_env_vars \
29+
MODEL \
30+
TP \
31+
DP_ATTENTION \
32+
CONC \
33+
ISL \
34+
OSL \
35+
MAX_MODEL_LEN \
36+
RANDOM_RANGE_RATIO \
37+
RESULT_FILENAME
38+
39+
if [[ -n "$SLURM_JOB_ID" ]]; then
40+
echo "JOB $SLURM_JOB_ID running on $SLURMD_NODENAME"
41+
fi
42+
43+
if [[ "$MODEL" != /* ]]; then hf download "$MODEL"; fi
44+
45+
if [ -n "$ROCR_VISIBLE_DEVICES" ]; then
46+
export HIP_VISIBLE_DEVICES="$ROCR_VISIBLE_DEVICES"
47+
fi
48+
49+
export VLLM_ROCM_USE_AITER=1
50+
export VLLM_ROCM_USE_AITER_MOE=1
51+
52+
# gsm8k eval at high concurrency (8k1k) OOM-kills the server: hundreds of
53+
# concurrent 9472-token requests exceed the ~20x KV budget even on 256GB MI325X
54+
# (c128 fit, so this was originally left at the default, but c512 crashed the
55+
# EngineCore mid-eval). Cap the eval to a safe in-flight count; only run_eval is
56+
# affected (throughput jobs use CONC directly). Matches the MI300X script.
57+
export EVAL_CONCURRENT_REQUESTS=8
58+
59+
SERVER_LOG=/workspace/server.log
60+
61+
if [ "${EVAL_ONLY}" = "true" ]; then
62+
setup_eval_context
63+
MAX_MODEL_LEN="$EVAL_MAX_MODEL_LEN"
64+
fi
65+
66+
start_gpu_monitor
67+
68+
PARALLEL_ARGS=(--tensor-parallel-size "$TP" --data-parallel-size 1)
69+
if [ "${DP_ATTENTION}" = "true" ]; then
70+
PARALLEL_ARGS=(--tensor-parallel-size 1 --data-parallel-size "$TP")
71+
fi
72+
73+
EP_ARGS=()
74+
if [ "${EP_SIZE:-1}" -gt 1 ]; then
75+
EP_ARGS=(--enable-expert-parallel)
76+
fi
77+
78+
set -x
79+
vllm serve $MODEL --port $PORT \
80+
"${PARALLEL_ARGS[@]}" \
81+
"${EP_ARGS[@]}" \
82+
--quantization deepseek_v4_fp8 \
83+
--async-scheduling \
84+
--no-enable-prefix-caching \
85+
--distributed-executor-backend mp \
86+
--gpu-memory-utilization 0.9 \
87+
--max-model-len "$MAX_MODEL_LEN" \
88+
--kv-cache-dtype fp8 \
89+
--trust-remote-code \
90+
--tokenizer-mode deepseek_v4 \
91+
--reasoning-parser deepseek_v4 \
92+
--compilation-config '{"mode":3,"cudagraph_mode":"FULL_AND_PIECEWISE"}' > $SERVER_LOG 2>&1 &
93+
94+
SERVER_PID=$!
95+
96+
wait_for_server_ready --port "$PORT" --server-log "$SERVER_LOG" --server-pid "$SERVER_PID"
97+
98+
run_benchmark_serving \
99+
--model "$MODEL" \
100+
--port "$PORT" \
101+
--backend vllm \
102+
--input-len "$ISL" \
103+
--output-len "$OSL" \
104+
--random-range-ratio "$RANDOM_RANGE_RATIO" \
105+
--num-prompts "$((CONC * 10))" \
106+
--max-concurrency "$CONC" \
107+
--result-filename "$RESULT_FILENAME" \
108+
--result-dir /workspace/ \
109+
--trust-remote-code
110+
111+
if [ "${RUN_EVAL}" = "true" ]; then
112+
run_eval --framework lm-eval --port "$PORT"
113+
append_lm_eval_summary
114+
fi
115+
116+
stop_gpu_monitor
117+
set +x
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
#!/usr/bin/env bash
2+
set -eo pipefail
3+
4+
# DeepSeek-V4-Pro FP8 single-node on MI325X (gfx942) via vLLM, MTP variant.
5+
#
6+
# MTP sibling of dsv4_fp8_mi325x.sh: adds --speculative-config
7+
# '{"method":"mtp","num_speculative_tokens":2}' (DeepSeek-V4 built-in MTP)
8+
# and --dsv4 chat-template encoding for run_benchmark_serving.
9+
#
10+
# EXTRAPOLATED bring-up recipe. The sglang path was abandoned: on gfx942
11+
# (no native FP4) the dsv4 sglang backend's nvfp4 MoE / TileLang-MLA kernels
12+
# have no gfx942 equivalents (they exist only for gfx950/MI355X). vLLM instead
13+
# runs the checkpoint in FP8 via --quantization deepseek_v4_fp8, which
14+
# dequantizes the FP4 MoE experts to FP8 — the same path the H200 dsv4 vLLM
15+
# recipe uses (H200 is also a no-FP4 SKU). Derived from:
16+
# * same model + framework + AMD family: dsv4_fp4_mi355x_vllm.sh (ROCm vLLM
17+
# dsv4 structure: AITER MoE, deepseek_v4 tokenizer/parser, mp executor,
18+
# FULL_AND_PIECEWISE compile)
19+
# * same model, FP8 path: dsv4_fp8_h200.sh (--quantization deepseek_v4_fp8)
20+
# * same SKU, different model: minimaxm3_fp8_mi325x.sh (gfx942 vLLM/AITER)
21+
#
22+
# The FP4->FP8 dequant roughly doubles the MoE footprint (~1.05 TB total),
23+
# which fits 8x256 GB comfortably at TP8, so the sweep is TP8-only.
24+
#
25+
# MoE backend is left at auto (NOT --moe-backend aiter) — see dsv4_fp8_mi300x.sh:
26+
# on gfx942, forcing aiter selects AITER_MXFP4_MXFP4 (W4A4 native-mxfp4) which
27+
# the gfx942 kernel rejects; auto's ROCm+DeepseekV4 path prefers
28+
# AITER_MXFP4_BF16 (W4A16, dequant) with a TRITON_UNFUSED fallback.
29+
30+
source "$(dirname "$0")/../../benchmark_lib.sh"
31+
32+
check_env_vars \
33+
MODEL \
34+
TP \
35+
DP_ATTENTION \
36+
CONC \
37+
ISL \
38+
OSL \
39+
MAX_MODEL_LEN \
40+
RANDOM_RANGE_RATIO \
41+
RESULT_FILENAME
42+
43+
if [[ -n "$SLURM_JOB_ID" ]]; then
44+
echo "JOB $SLURM_JOB_ID running on $SLURMD_NODENAME"
45+
fi
46+
47+
if [[ "$MODEL" != /* ]]; then hf download "$MODEL"; fi
48+
49+
if [ -n "$ROCR_VISIBLE_DEVICES" ]; then
50+
export HIP_VISIBLE_DEVICES="$ROCR_VISIBLE_DEVICES"
51+
fi
52+
53+
export VLLM_ROCM_USE_AITER=1
54+
export VLLM_ROCM_USE_AITER_MOE=1
55+
56+
# gsm8k eval at high concurrency (8k1k) OOM-kills the server: hundreds of
57+
# concurrent 9472-token requests exceed the ~20x KV budget even on 256GB MI325X
58+
# (c128 fit, so this was originally left at the default, but c512 crashed the
59+
# EngineCore mid-eval). Cap the eval to a safe in-flight count; only run_eval is
60+
# affected (throughput jobs use CONC directly). Matches the MI300X script.
61+
export EVAL_CONCURRENT_REQUESTS=8
62+
63+
SERVER_LOG=/workspace/server.log
64+
65+
if [ "${EVAL_ONLY}" = "true" ]; then
66+
setup_eval_context
67+
MAX_MODEL_LEN="$EVAL_MAX_MODEL_LEN"
68+
fi
69+
70+
start_gpu_monitor
71+
72+
PARALLEL_ARGS=(--tensor-parallel-size "$TP" --data-parallel-size 1)
73+
if [ "${DP_ATTENTION}" = "true" ]; then
74+
PARALLEL_ARGS=(--tensor-parallel-size 1 --data-parallel-size "$TP")
75+
fi
76+
77+
EP_ARGS=()
78+
if [ "${EP_SIZE:-1}" -gt 1 ]; then
79+
EP_ARGS=(--enable-expert-parallel)
80+
fi
81+
82+
# Use 2 speculative tokens (matches dsv4_fp4_mi355x_vllm_mtp.sh).
83+
NUM_SPEC_TOKENS=2
84+
85+
set -x
86+
vllm serve $MODEL --port $PORT \
87+
"${PARALLEL_ARGS[@]}" \
88+
"${EP_ARGS[@]}" \
89+
--quantization deepseek_v4_fp8 \
90+
--async-scheduling \
91+
--no-enable-prefix-caching \
92+
--distributed-executor-backend mp \
93+
--gpu-memory-utilization 0.9 \
94+
--max-model-len "$MAX_MODEL_LEN" \
95+
--kv-cache-dtype fp8 \
96+
--trust-remote-code \
97+
--tokenizer-mode deepseek_v4 \
98+
--reasoning-parser deepseek_v4 \
99+
--speculative-config "{\"method\": \"mtp\", \"num_speculative_tokens\": $NUM_SPEC_TOKENS}" \
100+
--compilation-config '{"mode":3,"cudagraph_mode":"FULL_AND_PIECEWISE"}' > $SERVER_LOG 2>&1 &
101+
102+
SERVER_PID=$!
103+
104+
wait_for_server_ready --port "$PORT" --server-log "$SERVER_LOG" --server-pid "$SERVER_PID"
105+
106+
run_benchmark_serving \
107+
--model "$MODEL" \
108+
--port "$PORT" \
109+
--backend vllm \
110+
--input-len "$ISL" \
111+
--output-len "$OSL" \
112+
--random-range-ratio "$RANDOM_RANGE_RATIO" \
113+
--num-prompts "$((CONC * 10))" \
114+
--max-concurrency "$CONC" \
115+
--result-filename "$RESULT_FILENAME" \
116+
--result-dir /workspace/ \
117+
--trust-remote-code \
118+
--dsv4
119+
120+
if [ "${RUN_EVAL}" = "true" ]; then
121+
run_eval --framework lm-eval --port "$PORT"
122+
append_lm_eval_summary
123+
fi
124+
125+
stop_gpu_monitor
126+
set +x

configs/amd-master.yaml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3149,3 +3149,61 @@ minimaxm3-fp8-mi325x-vllm-agentic:
31493149
- { tp: 4, ep: 4, kv-offloading: dram, kv-offload-backend: { name: mooncake, version: "0.3.11.post1" }, conc-list: [3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16] }
31503150
- { tp: 8, ep: 8, kv-offloading: dram, kv-offload-backend: { name: mooncake, version: "0.3.11.post1" }, conc-list: [10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 32] }
31513151
- { tp: 8, ep: 8, dp-attn: true, kv-offloading: dram, kv-offload-backend: { name: mooncake, version: "0.3.11.post1" }, conc-list: [24, 32, 36, 40, 44, 48, 52, 56, 60, 64, 72, 80, 96], router: { name: vllm-router, version: "0.1.14" } }
3152+
3153+
# DeepSeek-V4-Pro FP8 single-node on MI325X (gfx942) via vLLM.
3154+
# EXTRAPOLATED bring-up. Same rationale as dsv4-fp8-mi300x-vllm: sglang has no
3155+
# gfx942 build of the dsv4 nvfp4 MoE / TileLang-MLA kernels, so vLLM runs the
3156+
# checkpoint in FP8 via --quantization deepseek_v4_fp8 (dequant FP4 MoE -> FP8),
3157+
# the H200 dsv4 vLLM path. Config mirrors the same-model dsv4-fp4-mi355x-vllm
3158+
# (TP8, conc 4-512); 8x256GB (2TB) has ample headroom for the ~1.05TB FP8
3159+
# footprint. Launch script dsv4_fp8_mi325x.sh carries the deepseek_v4 + gfx942
3160+
# AITER flags.
3161+
dsv4-fp8-mi325x-vllm:
3162+
image: vllm/vllm-openai-rocm:nightly-09663abde0f50944a8d5ea30120666024b503faa
3163+
model: deepseek-ai/DeepSeek-V4-Pro
3164+
model-prefix: dsv4
3165+
# mi325x (mi325x-amds pool) runners are down; use the mi325x-tw cluster.
3166+
runner: cluster:mi325x-tw
3167+
precision: fp8
3168+
framework: vllm
3169+
multinode: false
3170+
scenarios:
3171+
fixed-seq-len:
3172+
- isl: 1024
3173+
osl: 1024
3174+
search-space:
3175+
- { tp: 8, conc-start: 4, conc-end: 512 }
3176+
- isl: 8192
3177+
osl: 1024
3178+
search-space:
3179+
# 8k1k KV fits only ~20x concurrency (9472-token requests), so conc512 is
3180+
# ~25x oversubscribed -> request timeouts. conc256 is proven green; cap here.
3181+
- { tp: 8, conc-start: 4, conc-end: 256 }
3182+
3183+
# MTP variant of dsv4-fp8-mi325x-vllm. Mirrors the base recipe and adds
3184+
# DeepSeek-V4 built-in MTP via --speculative-config (num_speculative_tokens=2),
3185+
# routing to dsv4_fp8_mi325x_mtp.sh; benchmark uses --dsv4 chat-template
3186+
# encoding (required for meaningful MTP acceptance).
3187+
dsv4-fp8-mi325x-vllm-mtp:
3188+
image: vllm/vllm-openai-rocm:nightly-09663abde0f50944a8d5ea30120666024b503faa
3189+
model: deepseek-ai/DeepSeek-V4-Pro
3190+
model-prefix: dsv4
3191+
# mi325x (mi325x-amds pool) runners are down; use the mi325x-tw cluster.
3192+
runner: cluster:mi325x-tw
3193+
precision: fp8
3194+
framework: vllm
3195+
multinode: false
3196+
scenarios:
3197+
fixed-seq-len:
3198+
- isl: 1024
3199+
osl: 1024
3200+
search-space:
3201+
- { tp: 8, conc-start: 4, conc-end: 512, spec-decoding: mtp }
3202+
- isl: 8192
3203+
osl: 1024
3204+
search-space:
3205+
# 8k1k MTP KV is tighter than normal (draft model): conc256 is borderline
3206+
# (MI300X 19.2% req failures, flaked pass->fail across runs) and conc512
3207+
# is 55.8%. conc128 passed cleanly on the memory-tightest SKU (MI300X);
3208+
# cap 8k1k MTP at 128 (normal holds 256, 1k1k holds 512).
3209+
- { tp: 8, conc-start: 4, conc-end: 128, spec-decoding: mtp }

perf-changelog.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4834,3 +4834,21 @@
48344834
- "Image: lmsysorg/sglang:v0.5.13.post1-cu130"
48354835
pr-link: https://github.com/SemiAnalysisAI/InferenceX/pull/2123
48364836

4837+
4838+
- config-keys:
4839+
- dsv4-fp8-mi325x-vllm
4840+
description:
4841+
- "Add DeepSeek-V4-Pro FP8 single-node MI325X vLLM benchmark (new SKU, previously no dsv4 data on gfx942)"
4842+
- "vLLM, not sglang: same rationale as dsv4-fp8-mi300x-vllm — sglang has no gfx942 build of the dsv4 nvfp4 MoE / TileLang-MLA kernels (gfx950/MI355X only). vLLM runs the checkpoint in FP8 via --quantization deepseek_v4_fp8 (dequant FP4 MoE -> FP8), the no-FP4 H200 dsv4 vLLM path"
4843+
- "Recipe mirrors the same-model dsv4-fp4-mi355x-vllm (ROCm vLLM dsv4: deepseek_v4 tokenizer/reasoning-parser, mp executor, FULL_AND_PIECEWISE compile) plus gfx942 AITER infra from minimaxm3-fp8-mi325x-vllm; TP8 conc 4-512, ample headroom on 8x256GB"
4844+
- "MoE backend left at auto (unlike MI355X's --moe-backend aiter): forcing aiter on gfx942 selects AITER_MXFP4_MXFP4 (W4A4 native-mxfp4) which the gfx942 kernel rejects; auto's ROCm+DeepseekV4 path prefers AITER_MXFP4_BF16 (W4A16, dequant) with TRITON_UNFUSED fallback (see dsv4-fp8-mi300x-vllm)"
4845+
- "Image vllm/vllm-openai-rocm:nightly-09663abde0f50944a8d5ea30120666024b503faa (the DSv4-validated ROCm vLLM build)"
4846+
pr-link: https://github.com/SemiAnalysisAI/InferenceX/pull/2195
4847+
4848+
- config-keys:
4849+
- dsv4-fp8-mi325x-vllm-mtp
4850+
description:
4851+
- "Add DeepSeek-V4-Pro FP8 MI325X vLLM MTP (speculative-decoding) variant, mirroring dsv4-fp8-mi325x-vllm plus --speculative-config {\"method\":\"mtp\",\"num_speculative_tokens\":2} (DeepSeek-V4 built-in MTP)"
4852+
- "run_benchmark_serving uses --dsv4 chat-template encoding, required for meaningful MTP acceptance rate; routes to dsv4_fp8_mi325x_mtp.sh via the launcher's spec-decoding=mtp suffix"
4853+
- "256GB MI325X keeps the default eval concurrency (no KV cap needed)"
4854+
pr-link: https://github.com/SemiAnalysisAI/InferenceX/pull/2195

runners/launch_mi325x-tw.sh

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#!/usr/bin/bash
2+
set -euo pipefail
3+
4+
# mi325x-tw is a NON-SLURM cluster: the runner executes on the GPU node itself
5+
# (docker + ROCm, no salloc/srun). So run the container directly on the node,
6+
# like launch_h100-cr.sh but AMD/ROCm (--device=/dev/kfd,/dev/dri instead of
7+
# --runtime=nvidia --gpus). dsv4 MI325X is single-node (TP8), so one node's
8+
# 8 GPUs suffice. Runs the same _mi325x.sh benchmark as the amds launcher.
9+
10+
HF_HUB_CACHE_MOUNT="${HF_HUB_CACHE_MOUNT:-/home/gharunner/hf_hub_cache/}"
11+
mkdir -p "$HF_HUB_CACHE_MOUNT"
12+
export HF_HUB_CACHE="${HF_HUB_CACHE:-/hf_hub_cache}"
13+
PORT=8888
14+
server_name="bmk-server-${RUNNER_NAME:-mi325x-tw}"
15+
16+
# Route spec-decoding=mtp configs to the _mtp benchmark script.
17+
SPEC_SUFFIX=$([[ "${SPEC_DECODING:-}" == "mtp" ]] && printf '_mtp' || printf '')
18+
19+
export GPU_COUNT="${GPU_COUNT:-${TP:?TP must be set}}"
20+
21+
set -x
22+
docker rm -f "$server_name" >/dev/null 2>&1 || true
23+
docker run --rm --network=host --name="$server_name" \
24+
--device=/dev/kfd --device=/dev/dri --group-add video --group-add render \
25+
--ipc=host --privileged --shm-size=32g --ulimit memlock=-1 --ulimit stack=67108864 \
26+
--security-opt seccomp=unconfined --cap-add=SYS_PTRACE \
27+
-v "$HF_HUB_CACHE_MOUNT:$HF_HUB_CACHE" \
28+
-v "$GITHUB_WORKSPACE:/workspace/" -w /workspace/ \
29+
-e HF_TOKEN -e HF_HUB_CACHE -e MODEL -e TP -e PP_SIZE -e DCP_SIZE -e PCP_SIZE -e GPU_COUNT -e CONC -e MAX_MODEL_LEN -e ISL -e OSL -e RUN_EVAL -e EVAL_ONLY -e RUNNER_TYPE -e RESULT_FILENAME -e RANDOM_RANGE_RATIO -e PORT="$PORT" \
30+
-e DP_ATTENTION -e EP_SIZE -e DP_SIZE -e EVAL_MAX_MODEL_LEN -e SPEC_DECODING -e NUM_SPEC_TOKENS \
31+
-e PROFILE -e SGLANG_TORCH_PROFILER_DIR -e VLLM_TORCH_PROFILER_DIR -e VLLM_RPC_TIMEOUT \
32+
-e PYTHONPYCACHEPREFIX=/tmp/pycache/ -e CUDA_DEVICE_ORDER=PCI_BUS_ID \
33+
--entrypoint=/bin/bash \
34+
"$IMAGE" \
35+
benchmarks/single_node/${SCENARIO_SUBDIR}"${EXP_NAME%%_*}_${PRECISION}_mi325x${SPEC_SUFFIX}.sh"

0 commit comments

Comments
 (0)