sskj-h3/throughput/vllm-omni-fasth3/scripts/run_fasth3_matrix_6000d.sh

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"