Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
4559de9
feat(dsv4): scaffold FP8 MI325X SGLang bring-up
Oseltamivir Jul 14, 2026
15e7c9d
Merge branch 'main' into dsv4-mi325x-fp8-bringup
Oseltamivir Jul 14, 2026
35e91f4
Update perf-changelog.yaml
Oseltamivir Jul 14, 2026
7bfc5cd
fix(dsv4): drop invalid --quantization from MI325X sglang script
Oseltamivir Jul 14, 2026
422d3a8
fix(dsv4): route MI325X MLA through unified_kv_triton FlashMLA
Oseltamivir Jul 14, 2026
7fd44e7
fix(dsv4): use triton MoE runner for MI325X (CK unsupported on gfx942)
Oseltamivir Jul 14, 2026
ec0fdfd
fix(dsv4): switch MI325X dsv4 to vLLM FP8 (sglang blocked on gfx942)
Oseltamivir Jul 14, 2026
f2b2fe6
fix(dsv4): MI325X vLLM — use auto MoE backend (AITER_MXFP4_BF16) on g…
Oseltamivir Jul 14, 2026
56767f1
feat(dsv4): add MI325X vLLM MTP spec-decode variant
Oseltamivir Jul 14, 2026
04d4f73
fix(dsv4): rebase MI325X perf-changelog onto origin/main (append-only)
Oseltamivir Jul 14, 2026
f454dc7
fix(dsv4): cap MI325X 8k1k conc at 256; clamp eval concurrency
Oseltamivir Jul 14, 2026
ec9b9af
fix(dsv4): lower MI325X MTP 8k1k conc cap 256->128
Oseltamivir Jul 14, 2026
78ad014
Merge branch 'main' into dsv4-mi325x-fp8-bringup
Oseltamivir Jul 15, 2026
4937d2c
fix(dsv4): route MI325X dsv4 to cluster:mi325x-tw (mi325x-amds down)
Oseltamivir Jul 15, 2026
b1b98d9
feat(runners): add launch_mi325x-tw.sh for the mi325x-tw cluster
Oseltamivir Jul 15, 2026
0a64423
fix(runners): make launch_mi325x-tw.sh a docker/ROCm launcher (no SLURM)
Oseltamivir Jul 15, 2026
0be916d
fix(runners): pass DP_ATTENTION + friends to mi325x-tw docker container
Oseltamivir Jul 15, 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
117 changes: 117 additions & 0 deletions benchmarks/single_node/fixed_seq_len/dsv4_fp8_mi325x.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#!/usr/bin/env bash
set -eo pipefail

# DeepSeek-V4-Pro FP8 single-node on MI325X (gfx942) via vLLM.
#
# EXTRAPOLATED bring-up recipe. The sglang path was abandoned: on gfx942
# (no native FP4) the dsv4 sglang backend's nvfp4 MoE / TileLang-MLA kernels
# have no gfx942 equivalents (they exist only for gfx950/MI355X). vLLM instead
# runs the checkpoint in FP8 via --quantization deepseek_v4_fp8, which
# dequantizes the FP4 MoE experts to FP8 — the same path the H200 dsv4 vLLM
# recipe uses (H200 is also a no-FP4 SKU). Derived from:
# * same model + framework + AMD family: dsv4_fp4_mi355x_vllm.sh (ROCm vLLM
# dsv4 structure: AITER MoE, deepseek_v4 tokenizer/parser, mp executor,
# FULL_AND_PIECEWISE compile)
# * same model, FP8 path: dsv4_fp8_h200.sh (--quantization deepseek_v4_fp8)
# * same SKU, different model: minimaxm3_fp8_mi325x.sh (gfx942 vLLM/AITER)
#
# The FP4->FP8 dequant roughly doubles the MoE footprint (~1.05 TB total),
# which fits 8x256 GB comfortably at TP8, so the sweep is TP8-only.
#
# MoE backend is left at auto (NOT --moe-backend aiter) — see dsv4_fp8_mi300x.sh:
# on gfx942, forcing aiter selects AITER_MXFP4_MXFP4 (W4A4 native-mxfp4) which
# the gfx942 kernel rejects; auto's ROCm+DeepseekV4 path prefers
# AITER_MXFP4_BF16 (W4A16, dequant) with a TRITON_UNFUSED fallback.

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

check_env_vars \
MODEL \
TP \
DP_ATTENTION \
CONC \
ISL \
OSL \
MAX_MODEL_LEN \
RANDOM_RANGE_RATIO \
RESULT_FILENAME

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

if [[ "$MODEL" != /* ]]; then hf download "$MODEL"; fi

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

export VLLM_ROCM_USE_AITER=1
export VLLM_ROCM_USE_AITER_MOE=1

# gsm8k eval at high concurrency (8k1k) OOM-kills the server: hundreds of
# concurrent 9472-token requests exceed the ~20x KV budget even on 256GB MI325X
# (c128 fit, so this was originally left at the default, but c512 crashed the
# EngineCore mid-eval). Cap the eval to a safe in-flight count; only run_eval is
# affected (throughput jobs use CONC directly). Matches the MI300X script.
export EVAL_CONCURRENT_REQUESTS=8

SERVER_LOG=/workspace/server.log

if [ "${EVAL_ONLY}" = "true" ]; then
setup_eval_context
MAX_MODEL_LEN="$EVAL_MAX_MODEL_LEN"
fi

start_gpu_monitor

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=()
if [ "${EP_SIZE:-1}" -gt 1 ]; then
EP_ARGS=(--enable-expert-parallel)
fi

set -x
vllm serve $MODEL --port $PORT \
"${PARALLEL_ARGS[@]}" \
"${EP_ARGS[@]}" \
--quantization deepseek_v4_fp8 \
--async-scheduling \
--no-enable-prefix-caching \
--distributed-executor-backend mp \
--gpu-memory-utilization 0.9 \
--max-model-len "$MAX_MODEL_LEN" \
--kv-cache-dtype fp8 \
--trust-remote-code \
--tokenizer-mode deepseek_v4 \
--reasoning-parser deepseek_v4 \
--compilation-config '{"mode":3,"cudagraph_mode":"FULL_AND_PIECEWISE"}' > $SERVER_LOG 2>&1 &

SERVER_PID=$!

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

run_benchmark_serving \
--model "$MODEL" \
--port "$PORT" \
--backend vllm \
--input-len "$ISL" \
--output-len "$OSL" \
--random-range-ratio "$RANDOM_RANGE_RATIO" \
--num-prompts "$((CONC * 10))" \
--max-concurrency "$CONC" \
--result-filename "$RESULT_FILENAME" \
--result-dir /workspace/ \
--trust-remote-code

if [ "${RUN_EVAL}" = "true" ]; then
run_eval --framework lm-eval --port "$PORT"
append_lm_eval_summary
fi

stop_gpu_monitor
set +x
126 changes: 126 additions & 0 deletions benchmarks/single_node/fixed_seq_len/dsv4_fp8_mi325x_mtp.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
#!/usr/bin/env bash
set -eo pipefail

# DeepSeek-V4-Pro FP8 single-node on MI325X (gfx942) via vLLM, MTP variant.
#
# MTP sibling of dsv4_fp8_mi325x.sh: adds --speculative-config
# '{"method":"mtp","num_speculative_tokens":2}' (DeepSeek-V4 built-in MTP)
# and --dsv4 chat-template encoding for run_benchmark_serving.
#
# EXTRAPOLATED bring-up recipe. The sglang path was abandoned: on gfx942
# (no native FP4) the dsv4 sglang backend's nvfp4 MoE / TileLang-MLA kernels
# have no gfx942 equivalents (they exist only for gfx950/MI355X). vLLM instead
# runs the checkpoint in FP8 via --quantization deepseek_v4_fp8, which
# dequantizes the FP4 MoE experts to FP8 — the same path the H200 dsv4 vLLM
# recipe uses (H200 is also a no-FP4 SKU). Derived from:
# * same model + framework + AMD family: dsv4_fp4_mi355x_vllm.sh (ROCm vLLM
# dsv4 structure: AITER MoE, deepseek_v4 tokenizer/parser, mp executor,
# FULL_AND_PIECEWISE compile)
# * same model, FP8 path: dsv4_fp8_h200.sh (--quantization deepseek_v4_fp8)
# * same SKU, different model: minimaxm3_fp8_mi325x.sh (gfx942 vLLM/AITER)
#
# The FP4->FP8 dequant roughly doubles the MoE footprint (~1.05 TB total),
# which fits 8x256 GB comfortably at TP8, so the sweep is TP8-only.
#
# MoE backend is left at auto (NOT --moe-backend aiter) — see dsv4_fp8_mi300x.sh:
# on gfx942, forcing aiter selects AITER_MXFP4_MXFP4 (W4A4 native-mxfp4) which
# the gfx942 kernel rejects; auto's ROCm+DeepseekV4 path prefers
# AITER_MXFP4_BF16 (W4A16, dequant) with a TRITON_UNFUSED fallback.

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

check_env_vars \
MODEL \
TP \
DP_ATTENTION \
CONC \
ISL \
OSL \
MAX_MODEL_LEN \
RANDOM_RANGE_RATIO \
RESULT_FILENAME

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

if [[ "$MODEL" != /* ]]; then hf download "$MODEL"; fi

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

export VLLM_ROCM_USE_AITER=1
export VLLM_ROCM_USE_AITER_MOE=1

# gsm8k eval at high concurrency (8k1k) OOM-kills the server: hundreds of
# concurrent 9472-token requests exceed the ~20x KV budget even on 256GB MI325X
# (c128 fit, so this was originally left at the default, but c512 crashed the
# EngineCore mid-eval). Cap the eval to a safe in-flight count; only run_eval is
# affected (throughput jobs use CONC directly). Matches the MI300X script.
export EVAL_CONCURRENT_REQUESTS=8

SERVER_LOG=/workspace/server.log

if [ "${EVAL_ONLY}" = "true" ]; then
setup_eval_context
MAX_MODEL_LEN="$EVAL_MAX_MODEL_LEN"
fi

start_gpu_monitor

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=()
if [ "${EP_SIZE:-1}" -gt 1 ]; then
EP_ARGS=(--enable-expert-parallel)
fi

# Use 2 speculative tokens (matches dsv4_fp4_mi355x_vllm_mtp.sh).
NUM_SPEC_TOKENS=2

set -x
vllm serve $MODEL --port $PORT \
"${PARALLEL_ARGS[@]}" \
"${EP_ARGS[@]}" \
--quantization deepseek_v4_fp8 \
--async-scheduling \
--no-enable-prefix-caching \
--distributed-executor-backend mp \
--gpu-memory-utilization 0.9 \
--max-model-len "$MAX_MODEL_LEN" \
--kv-cache-dtype fp8 \
--trust-remote-code \
--tokenizer-mode deepseek_v4 \
--reasoning-parser deepseek_v4 \
--speculative-config "{\"method\": \"mtp\", \"num_speculative_tokens\": $NUM_SPEC_TOKENS}" \
--compilation-config '{"mode":3,"cudagraph_mode":"FULL_AND_PIECEWISE"}' > $SERVER_LOG 2>&1 &

SERVER_PID=$!

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

run_benchmark_serving \
--model "$MODEL" \
--port "$PORT" \
--backend vllm \
--input-len "$ISL" \
--output-len "$OSL" \
--random-range-ratio "$RANDOM_RANGE_RATIO" \
--num-prompts "$((CONC * 10))" \
--max-concurrency "$CONC" \
--result-filename "$RESULT_FILENAME" \
--result-dir /workspace/ \
--trust-remote-code \
--dsv4

if [ "${RUN_EVAL}" = "true" ]; then
run_eval --framework lm-eval --port "$PORT"
append_lm_eval_summary
fi

stop_gpu_monitor
set +x
58 changes: 58 additions & 0 deletions configs/amd-master.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,64 @@ dsr1-fp8-mi325x-sglang:
search-space:
- { tp: 8, conc-start: 4, conc-end: 64 }

# DeepSeek-V4-Pro FP8 single-node on MI325X (gfx942) via vLLM.
# EXTRAPOLATED bring-up. Same rationale as dsv4-fp8-mi300x-vllm: sglang has no
# gfx942 build of the dsv4 nvfp4 MoE / TileLang-MLA kernels, so vLLM runs the
# checkpoint in FP8 via --quantization deepseek_v4_fp8 (dequant FP4 MoE -> FP8),
# the H200 dsv4 vLLM path. Config mirrors the same-model dsv4-fp4-mi355x-vllm
# (TP8, conc 4-512); 8x256GB (2TB) has ample headroom for the ~1.05TB FP8
# footprint. Launch script dsv4_fp8_mi325x.sh carries the deepseek_v4 + gfx942
# AITER flags.
dsv4-fp8-mi325x-vllm:
image: vllm/vllm-openai-rocm:nightly-09663abde0f50944a8d5ea30120666024b503faa
model: deepseek-ai/DeepSeek-V4-Pro
model-prefix: dsv4
# mi325x (mi325x-amds pool) runners are down; use the mi325x-tw cluster.
runner: cluster:mi325x-tw
precision: fp8
framework: vllm
multinode: false
scenarios:
fixed-seq-len:
- isl: 1024
osl: 1024
search-space:
- { tp: 8, conc-start: 4, conc-end: 512 }
- isl: 8192
osl: 1024
search-space:
# 8k1k KV fits only ~20x concurrency (9472-token requests), so conc512 is
# ~25x oversubscribed -> request timeouts. conc256 is proven green; cap here.
- { tp: 8, conc-start: 4, conc-end: 256 }

# MTP variant of dsv4-fp8-mi325x-vllm. Mirrors the base recipe and adds
# DeepSeek-V4 built-in MTP via --speculative-config (num_speculative_tokens=2),
# routing to dsv4_fp8_mi325x_mtp.sh; benchmark uses --dsv4 chat-template
# encoding (required for meaningful MTP acceptance).
dsv4-fp8-mi325x-vllm-mtp:
image: vllm/vllm-openai-rocm:nightly-09663abde0f50944a8d5ea30120666024b503faa
model: deepseek-ai/DeepSeek-V4-Pro
model-prefix: dsv4
# mi325x (mi325x-amds pool) runners are down; use the mi325x-tw cluster.
runner: cluster:mi325x-tw
precision: fp8
framework: vllm
multinode: false
scenarios:
fixed-seq-len:
- isl: 1024
osl: 1024
search-space:
- { tp: 8, conc-start: 4, conc-end: 512, spec-decoding: mtp }
- isl: 8192
osl: 1024
search-space:
# 8k1k MTP KV is tighter than normal (draft model): conc256 is borderline
# (MI300X 19.2% req failures, flaked pass->fail across runs) and conc512
# is 55.8%. conc128 passed cleanly on the memory-tightest SKU (MI300X);
# cap 8k1k MTP at 128 (normal holds 256, 1k1k holds 512).
- { tp: 8, conc-start: 4, conc-end: 128, spec-decoding: mtp }

dsr1-fp8-mi355x-sglang:
image: lmsysorg/sglang:v0.5.12-rocm700-mi35x
model: deepseek-ai/DeepSeek-R1-0528
Expand Down
18 changes: 18 additions & 0 deletions perf-changelog.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4781,3 +4781,21 @@
- "Bump image to lmsysorg/sglang-rocm:v0.5.14-rocm720-mi35x-20260708"
- "Clean the export envs"
pr-link: https://github.com/SemiAnalysisAI/InferenceX/pull/2198

- config-keys:
- dsv4-fp8-mi325x-vllm
description:
- "Add DeepSeek-V4-Pro FP8 single-node MI325X vLLM benchmark (new SKU, previously no dsv4 data on gfx942)"
- "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"
- "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"
- "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)"
- "Image vllm/vllm-openai-rocm:nightly-09663abde0f50944a8d5ea30120666024b503faa (the DSv4-validated ROCm vLLM build)"
pr-link: https://github.com/SemiAnalysisAI/InferenceX/pull/2195

- config-keys:
- dsv4-fp8-mi325x-vllm-mtp
description:
- "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)"
- "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"
- "256GB MI325X keeps the default eval concurrency (no KV cap needed)"
pr-link: https://github.com/SemiAnalysisAI/InferenceX/pull/2195
35 changes: 35 additions & 0 deletions runners/launch_mi325x-tw.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/usr/bin/bash
set -euo pipefail

# mi325x-tw is a NON-SLURM cluster: the runner executes on the GPU node itself
# (docker + ROCm, no salloc/srun). So run the container directly on the node,
# like launch_h100-cr.sh but AMD/ROCm (--device=/dev/kfd,/dev/dri instead of
# --runtime=nvidia --gpus). dsv4 MI325X is single-node (TP8), so one node's
# 8 GPUs suffice. Runs the same _mi325x.sh benchmark as the amds launcher.

HF_HUB_CACHE_MOUNT="${HF_HUB_CACHE_MOUNT:-/home/gharunner/hf_hub_cache/}"
mkdir -p "$HF_HUB_CACHE_MOUNT"
export HF_HUB_CACHE="${HF_HUB_CACHE:-/hf_hub_cache}"
PORT=8888
server_name="bmk-server-${RUNNER_NAME:-mi325x-tw}"

# Route spec-decoding=mtp configs to the _mtp benchmark script.
SPEC_SUFFIX=$([[ "${SPEC_DECODING:-}" == "mtp" ]] && printf '_mtp' || printf '')

export GPU_COUNT="${GPU_COUNT:-${TP:?TP must be set}}"

set -x
docker rm -f "$server_name" >/dev/null 2>&1 || true
docker run --rm --network=host --name="$server_name" \
--device=/dev/kfd --device=/dev/dri --group-add video --group-add render \
--ipc=host --privileged --shm-size=32g --ulimit memlock=-1 --ulimit stack=67108864 \
--security-opt seccomp=unconfined --cap-add=SYS_PTRACE \
-v "$HF_HUB_CACHE_MOUNT:$HF_HUB_CACHE" \
-v "$GITHUB_WORKSPACE:/workspace/" -w /workspace/ \
-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" \
-e DP_ATTENTION -e EP_SIZE -e DP_SIZE -e EVAL_MAX_MODEL_LEN -e SPEC_DECODING -e NUM_SPEC_TOKENS \
-e PROFILE -e SGLANG_TORCH_PROFILER_DIR -e VLLM_TORCH_PROFILER_DIR -e VLLM_RPC_TIMEOUT \
-e PYTHONPYCACHEPREFIX=/tmp/pycache/ -e CUDA_DEVICE_ORDER=PCI_BUS_ID \
--entrypoint=/bin/bash \
"$IMAGE" \
benchmarks/single_node/${SCENARIO_SUBDIR}"${EXP_NAME%%_*}_${PRECISION}_mi325x${SPEC_SUFFIX}.sh"