247 lines
11 KiB
Bash
Executable File
247 lines
11 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# MiniMax-H3 FastH3 (4-step distilled T2VA) benchmark on one 8-GPU RTX 6000D host.
|
|
# Derived from vllm-omni-base's run_vllm_omni_h3_matrix_6000d.sh; differences:
|
|
# * serves the MiniMax-H3 root with the FastH3 four-step adapter
|
|
# (--lora-path + --lora-backend distill, fused at load time);
|
|
# * task scope: Dense/Data-Free T2VA only (FastH3 v1 serving contract);
|
|
# * 4 inference steps (5 sigma positions / 4 DiT forwards) by default.
|
|
# The 8/4/2 labels mean GPUs per model instance: TP2xUSP4, TP2xUSP2, TP2xUSP1.
|
|
set -Eeuo pipefail
|
|
|
|
TOTAL_GPUS=${TOTAL_GPUS:-8}
|
|
GPU_MATRIX=${GPU_MATRIX:-"8 4 2"}
|
|
TASKS=${TASKS:-"t2va"}
|
|
RESOLUTION_MAP=${RESOLUTION_MAP:-"480:864x480,720:1280x736,768:1344x768,1080:1920x1088"}
|
|
REQUESTS_PER_RESOLUTION=${REQUESTS_PER_RESOLUTION:-8}
|
|
REQUESTS_PER_TASK=$((REQUESTS_PER_RESOLUTION * 4))
|
|
TOTAL_REQUESTS_PER_CONFIG=$((REQUESTS_PER_TASK * 1))
|
|
NUM_INFERENCE_STEPS=${NUM_INFERENCE_STEPS:-4}
|
|
DURATION_SECONDS=${DURATION_SECONDS:-15}
|
|
WARMUP_REQUESTS=${WARMUP_REQUESTS:-2}
|
|
WARMUP_INFERENCE_STEPS=${WARMUP_INFERENCE_STEPS:-4}
|
|
|
|
HOST=${HOST:-127.0.0.1}
|
|
BASE_PORT=${BASE_PORT:-30010}
|
|
PORT_STRIDE=${PORT_STRIDE:-100}
|
|
VLLM_INTERNAL_PORT_BASE=${VLLM_INTERNAL_PORT_BASE:-35000}
|
|
OMNI_MASTER_PORT_BASE=${OMNI_MASTER_PORT_BASE:-36000}
|
|
SERVER_START_TIMEOUT=${SERVER_START_TIMEOUT:-1800}
|
|
VIDEO_REQUEST_TIMEOUT=${VIDEO_REQUEST_TIMEOUT:-14400}
|
|
|
|
MODEL_ROOT=${MODEL_ROOT:-/data/hf_models/MiniMax-H3}
|
|
FASTH3_ADAPTER=${FASTH3_ADAPTER:-/data/hf_models/FastVideo-FastH3-4-step-Preview-v1-LoRA/dense-datafree/adapter_model.safetensors}
|
|
REFERENCE_IMAGE=${REFERENCE_IMAGE:-/data/wxy/sskj-MiniMax-H3/assets/reference_images/landscape_mountain_lake.jpg}
|
|
PROMPT_FILE=${PROMPT_FILE:-/root/.cache/sglang/vbench_subject_consistency.txt}
|
|
VLLM_ENV=${VLLM_ENV:-/root/.miniconda3/envs/vllm-omni}
|
|
PYTHON=${PYTHON:-$VLLM_ENV/bin/python}
|
|
VLLM_BIN=${VLLM_BIN:-$VLLM_ENV/bin/vllm}
|
|
CLIENT_SCRIPT=${CLIENT_SCRIPT:-/data/wxy/sskj-h3/throughput/vllm-omni-fasth3/scripts/minimax_h3_t2va_bench.py}
|
|
REPLICA_LAUNCHER=${REPLICA_LAUNCHER:-/root/fasth3_replica.sh}
|
|
RUN_ID=${RUN_ID:-fasth3-$(date '+%Y%m%d-%H%M%S')}
|
|
RESULT_ROOT=${RESULT_ROOT:-/data/wxy/results/minimax_h3_fasth3_matrix/$RUN_ID}
|
|
|
|
export PATH="$VLLM_ENV/bin:$PATH"
|
|
export LD_LIBRARY_PATH="$VLLM_ENV/lib:${LD_LIBRARY_PATH:-}"
|
|
|
|
declare -a SERVER_PIDS=()
|
|
declare -a CLIENT_PIDS=()
|
|
|
|
log() { printf '[%s] %s\n' "$(date '+%F %T')" "$*"; }
|
|
die() { log "ERROR: $*" >&2; exit 1; }
|
|
|
|
[[ -x "$PYTHON" ]] || die "python not executable: $PYTHON"
|
|
[[ -x "$VLLM_BIN" ]] || die "vllm not executable: $VLLM_BIN"
|
|
[[ -f "$CLIENT_SCRIPT" ]] || die "client script missing: $CLIENT_SCRIPT"
|
|
[[ -f "$FASTH3_ADAPTER" ]] || die "FastH3 adapter missing: $FASTH3_ADAPTER"
|
|
[[ -d "$MODEL_ROOT/transformer" ]] || die "MiniMax-H3 root checkpoint missing under $MODEL_ROOT"
|
|
[[ -f "$REFERENCE_IMAGE" ]] || die "reference image missing: $REFERENCE_IMAGE"
|
|
[[ -s "$PROMPT_FILE" ]] || die "VBench prompt file missing or empty: $PROMPT_FILE"
|
|
|
|
mkdir -p "$RESULT_ROOT"
|
|
SUMMARY_TSV="$RESULT_ROOT/summary.tsv"
|
|
printf 'gpus_per_instance\tdit_tp\tusp\treplicas\ttask\texpected\trecorded\tcompleted\tfailed\tmachine_qps\tlatency_mean_s\tlatency_p95_s\tmachine_wall_s\n' > "$SUMMARY_TSV"
|
|
|
|
port_is_open() {
|
|
"$PYTHON" - "$HOST" "$1" <<'PY'
|
|
import socket, sys
|
|
s = socket.socket(); s.settimeout(0.5)
|
|
try: s.connect((sys.argv[1], int(sys.argv[2])))
|
|
except OSError: raise SystemExit(1)
|
|
else: raise SystemExit(0)
|
|
finally: s.close()
|
|
PY
|
|
}
|
|
|
|
stop_servers() {
|
|
local pid alive deadline
|
|
((${#SERVER_PIDS[@]})) || return 0
|
|
log "gracefully stopping ${#SERVER_PIDS[@]} vLLM-Omni server(s)"
|
|
for pid in "${SERVER_PIDS[@]}"; do
|
|
kill -TERM -- "-$pid" 2>/dev/null || kill -TERM "$pid" 2>/dev/null || true
|
|
done
|
|
deadline=$((SECONDS + 180))
|
|
while ((SECONDS < deadline)); do
|
|
alive=0
|
|
for pid in "${SERVER_PIDS[@]}"; do kill -0 "$pid" 2>/dev/null && alive=1; done
|
|
((alive == 0)) && break
|
|
sleep 2
|
|
done
|
|
for pid in "${SERVER_PIDS[@]}"; do
|
|
if kill -0 "$pid" 2>/dev/null; then
|
|
log "server pid=$pid did not exit after SIGTERM; killing process group"
|
|
kill -KILL -- "-$pid" 2>/dev/null || kill -KILL "$pid" 2>/dev/null || true
|
|
fi
|
|
wait "$pid" 2>/dev/null || true
|
|
done
|
|
SERVER_PIDS=()
|
|
}
|
|
|
|
cleanup() {
|
|
local rc=$? pid
|
|
trap - EXIT INT TERM
|
|
for pid in "${CLIENT_PIDS[@]}"; do kill -TERM "$pid" 2>/dev/null || true; done
|
|
stop_servers
|
|
exit "$rc"
|
|
}
|
|
trap cleanup EXIT INT TERM
|
|
|
|
wait_healthy() {
|
|
local port=$1 pid=$2 log_file=$3 deadline=$((SECONDS + SERVER_START_TIMEOUT))
|
|
while ((SECONDS < deadline)); do
|
|
curl -fsS --max-time 5 "http://${HOST}:${port}/health" >/dev/null 2>&1 && return 0
|
|
if ! kill -0 "$pid" 2>/dev/null; then
|
|
tail -120 "$log_file" >&2 || true
|
|
return 1
|
|
fi
|
|
sleep 5
|
|
done
|
|
tail -120 "$log_file" >&2 || true
|
|
return 1
|
|
}
|
|
|
|
parallel_layout() {
|
|
local gpus_per_instance=$1
|
|
case "$gpus_per_instance" in
|
|
2) printf '2 1 2 2\n' ;;
|
|
4) printf '2 2 4 4\n' ;;
|
|
8) printf '2 4 8 8\n' ;;
|
|
*) die "supported GPUs per instance are 2, 4, and 8; got $gpus_per_instance" ;;
|
|
esac
|
|
}
|
|
|
|
model_dir_for() {
|
|
# t2va uses the MiniMax-H3 root; fl2va/ref2va use the per-task subdirs.
|
|
case "$1" in
|
|
t2va) printf '%s\n' "$MODEL_ROOT" ;;
|
|
fl2va) printf '%s/FL2VA\n' "$MODEL_ROOT" ;;
|
|
ref2va) printf '%s/Ref2VA\n' "$MODEL_ROOT" ;;
|
|
*) die "unknown task: $1" ;;
|
|
esac
|
|
}
|
|
|
|
start_servers() {
|
|
local gpus_per_instance=$1 replicas=$2 task=$3 phase_dir=$4
|
|
local model_dir dit_tp usp text_tp vae_pp replica port internal_port omni_port
|
|
local first_gpu offset gpu gpu_csv server_dir server_log rpc_dir candidate launcher_pid
|
|
model_dir=$(model_dir_for "$task")
|
|
read -r dit_tp usp text_tp vae_pp < <(parallel_layout "$gpus_per_instance")
|
|
SERVER_PIDS=()
|
|
for ((replica=0; replica<replicas; replica++)); do
|
|
port=$((BASE_PORT + replica * PORT_STRIDE))
|
|
internal_port=$((VLLM_INTERNAL_PORT_BASE + replica * PORT_STRIDE))
|
|
omni_port=$((OMNI_MASTER_PORT_BASE + replica * PORT_STRIDE))
|
|
for candidate in "$port" "$internal_port" "$omni_port"; do
|
|
port_is_open "$candidate" && die "port already in use: $candidate"
|
|
done
|
|
first_gpu=$((replica * gpus_per_instance)); gpu_csv=""
|
|
for ((offset=0; offset<gpus_per_instance; offset++)); do
|
|
gpu=$((first_gpu + offset))
|
|
[[ -z "$gpu_csv" ]] && gpu_csv="$gpu" || gpu_csv+=",$gpu"
|
|
done
|
|
server_dir="$phase_dir/server_${replica}_port${port}"
|
|
# Unix-domain socket paths are limited to roughly 107 characters on Linux.
|
|
# Keep vLLM's RPC base independent of the deliberately descriptive result path.
|
|
rpc_dir=$(mktemp -d /tmp/vh3rpc.XXXXXX)
|
|
mkdir -p "$server_dir/outputs"
|
|
printf '%s\n' "$rpc_dir" > "$server_dir/vllm_rpc_base_path.txt"
|
|
server_log="$server_dir/server.log"
|
|
printf '%s\n' "$gpu_csv" > "$server_dir/cuda_visible_devices.txt"
|
|
log "starting task=$task replica=$replica GPUs=$gpu_csv port=$port TP=$dit_tp USP=$usp model=$model_dir"
|
|
# Replicas are launched through a dedicated launcher script (not an inline
|
|
# env-prefix command): inline `VAR=... setsid ... &` inside a function
|
|
# changed the loaded-memory path (encoder eager-materialized -> ~82 GiB
|
|
# load peak -> CUDA OOM on the 83 GiB cards), while the launcher form
|
|
# reproduces the canonical ~66 GiB load. Startup is serialized so each
|
|
# replica loads alone and the load peak never overlaps.
|
|
setsid bash "$REPLICA_LAUNCHER" \
|
|
"$gpu_csv" "$port" "$internal_port" "$omni_port" "$rpc_dir" "$server_log" \
|
|
>/dev/null 2>&1 &
|
|
launcher_pid=$!
|
|
SERVER_PIDS+=("$launcher_pid")
|
|
wait_healthy "$port" "$launcher_pid" "$server_log" \
|
|
|| die "task=$task replica=$replica failed startup"
|
|
log "task=$task replica=$replica healthy port=$port"
|
|
done
|
|
}
|
|
|
|
run_clients() {
|
|
local gpus_per_instance=$1 replicas=$2 task=$3 phase_dir=$4
|
|
local dit_tp usp text_tp vae_pp replica port client_dir failed=0 ref_args=()
|
|
read -r dit_tp usp text_tp vae_pp < <(parallel_layout "$gpus_per_instance")
|
|
if [[ "$task" != t2va ]]; then ref_args=(--reference-image "$REFERENCE_IMAGE"); fi
|
|
CLIENT_PIDS=()
|
|
for ((replica=0; replica<replicas; replica++)); do
|
|
port=$((BASE_PORT + replica * PORT_STRIDE))
|
|
client_dir="$phase_dir/client_${replica}_port${port}"
|
|
mkdir -p "$client_dir/videos"
|
|
"$PYTHON" "$CLIENT_SCRIPT" run \
|
|
--host "$HOST" --port "$port" --task "$task" \
|
|
"${ref_args[@]}" --prompt-file "$PROMPT_FILE" \
|
|
--resolution-map "$RESOLUTION_MAP" --requests-per-resolution "$REQUESTS_PER_RESOLUTION" \
|
|
--replica-index "$replica" --num-replicas "$replicas" \
|
|
--gpus-per-instance "$gpus_per_instance" --dit-tp "$dit_tp" --usp "$usp" \
|
|
--num-inference-steps "$NUM_INFERENCE_STEPS" \
|
|
--warmup-requests "$WARMUP_REQUESTS" --warmup-inference-steps "$WARMUP_INFERENCE_STEPS" \
|
|
--duration-seconds "$DURATION_SECONDS" --aspect-ratio 16:9 \
|
|
--request-timeout "$VIDEO_REQUEST_TIMEOUT" \
|
|
--video-dir "$client_dir/videos" --output "$client_dir/results.jsonl" \
|
|
>"$client_dir/client.log" 2>&1 &
|
|
CLIENT_PIDS+=("$!")
|
|
log "started task=$task client=$replica port=$port requests=$((REQUESTS_PER_TASK / replicas))"
|
|
done
|
|
for ((replica=0; replica<replicas; replica++)); do
|
|
wait "${CLIENT_PIDS[$replica]}" || failed=1
|
|
done
|
|
CLIENT_PIDS=()
|
|
"$PYTHON" "$CLIENT_SCRIPT" summarize \
|
|
--input-dir "$phase_dir" --output "$phase_dir/summary.json" \
|
|
--task "$task" --gpus-per-instance "$gpus_per_instance" \
|
|
--dit-tp "$dit_tp" --usp "$usp" --replicas "$replicas" \
|
|
--expected-requests "$REQUESTS_PER_TASK" >> "$SUMMARY_TSV" || failed=1
|
|
return "$failed"
|
|
}
|
|
|
|
read -r -a GPU_VALUES <<< "$GPU_MATRIX"
|
|
read -r -a TASK_VALUES <<< "$TASKS"
|
|
for gpus_per_instance in "${GPU_VALUES[@]}"; do
|
|
((TOTAL_GPUS % gpus_per_instance == 0)) \
|
|
|| die "TOTAL_GPUS=$TOTAL_GPUS not divisible by gpus_per_instance=$gpus_per_instance"
|
|
replicas=$((TOTAL_GPUS / gpus_per_instance))
|
|
((REQUESTS_PER_RESOLUTION % replicas == 0)) \
|
|
|| die "REQUESTS_PER_RESOLUTION=$REQUESTS_PER_RESOLUTION not divisible by replicas=$replicas"
|
|
read -r dit_tp usp text_tp vae_pp < <(parallel_layout "$gpus_per_instance")
|
|
log "===== GPUs/instance=$gpus_per_instance TP=$dit_tp USP=$usp replicas=$replicas total_requests=$TOTAL_REQUESTS_PER_CONFIG steps=$NUM_INFERENCE_STEPS duration=${DURATION_SECONDS}s ====="
|
|
for task in "${TASK_VALUES[@]}"; do
|
|
phase_dir="$RESULT_ROOT/gpus${gpus_per_instance}_tp${dit_tp}_usp${usp}_replicas${replicas}/${task}"
|
|
mkdir -p "$phase_dir"
|
|
start_servers "$gpus_per_instance" "$replicas" "$task" "$phase_dir"
|
|
phase_failed=0
|
|
run_clients "$gpus_per_instance" "$replicas" "$task" "$phase_dir" || phase_failed=1
|
|
stop_servers
|
|
((phase_failed == 0)) \
|
|
|| die "GPUs/instance=$gpus_per_instance task=$task failed; inspect $phase_dir"
|
|
done
|
|
done
|
|
|
|
trap - EXIT INT TERM
|
|
log "FastH3 matrix complete: $SUMMARY_TSV" |