#!/bin/bash # 5-stage barrier orchestrator for connector_tax microbench. # # Stage 0 — pre-flight + (optional) step-timing patch # Stage 1 — Phase A (rate sweep) for all configs # Stage 2 — pick reference rates from Phase A # Stage 3 — Phase B (shape sweep at ref_safe) for all configs # Stage 4 — revert patch + analyze + plot # # Configurable via env: # CT_DATE : run-id directory tag (default $(date +%Y%m%d_%H%M)) # PORT : vLLM port (default 8000) # GPU_ID : single GPU index (default 0) # MODEL_PATH : path to model # PHASE_A_RATES : default 0.5,1,2,4,8,16,32 # PHASE_B_SHAPES : default 512x64,512x256,512x1024,4096x64,4096x256,4096x1024,32768x64,32768x256,32768x1024 # MIN_COMPLETED : default 200 # DURATION : default 60 # SKIP_PATCH : set to 1 to bypass scheduler patch # STAGES : space-separated list of stages to run, e.g. "1 3 4" # defaults to "0 1 2 3 4" set -uo pipefail HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" RESULTS_ROOT="$HERE/results" RUN_DATE="${CT_DATE:-$(date +%Y%m%d_%H%M)}" RUN_ROOT="$RESULTS_ROOT/$RUN_DATE" mkdir -p "$RUN_ROOT" PORT="${PORT:-8000}" GPU_ID="${GPU_ID:-0}" MODEL_PATH="${MODEL_PATH:-$HOME/models/Qwen/Qwen3-Coder-30B-A3B-Instruct}" PHASE_A_RATES="${PHASE_A_RATES:-0.5,1,2,4,8,16,32}" PHASE_B_SHAPES="${PHASE_B_SHAPES:-512x64,512x256,512x1024,4096x64,4096x256,4096x1024,32768x64,32768x256,32768x1024}" MIN_COMPLETED="${MIN_COMPLETED:-200}" DURATION="${DURATION:-60}" STAGES="${STAGES:-0 1 2 3 4}" ALL_CONFIGS=(plain noop_connector mooncake_producer mooncake_consumer mooncake_both nixl_both lmcache_only multi_mooncake_lmcache) # Shuffle config order for thermal-drift robustness. shuffle_configs() { printf '%s\n' "$@" | shuf } PYTHON="$(cd "$HERE/../.." && pwd)/.venv/bin/python" [[ -x "$PYTHON" ]] || PYTHON="$(command -v python3)" PROJ_DIR="$(cd "$HERE/../.." && pwd)" export PYTHONPATH="$PROJ_DIR:${PYTHONPATH:-}" manifest() { local config="$1" stage="$2" status="$3" note="$4" echo "$(date -Iseconds) | $config | $stage | $status | $note" \ >> "$RUN_ROOT/MANIFEST.tsv" } kill_vllm() { local pidfile="$1" # Kill the API server PID if [[ -f "$pidfile" ]]; then local pid; pid=$(cat "$pidfile") if [[ -n "$pid" ]]; then kill -9 "$pid" 2>/dev/null || true fi fi # Also kill any vLLM/EngineCore processes on this port or GPU pkill -9 -f "port $PORT" 2>/dev/null || true pkill -9 -f "VLLM::EngineCore" 2>/dev/null || true pkill -9 -f "vllm.entrypoints" 2>/dev/null || true sleep 5 # Wait for GPU memory release (up to 60s) for _ in $(seq 1 30); do used=$(nvidia-smi --query-gpu=memory.used --format=csv,noheader,nounits -i "$GPU_ID" 2>/dev/null | tr -d ' ') if [[ -n "$used" && "$used" -lt 1000 ]]; then return 0 fi # Try killing any remaining GPU holders fuser -k "/dev/nvidia${GPU_ID}" 2>/dev/null || true sleep 2 done echo "WARNING: GPU $GPU_ID still not free after 60s" >&2 } run_phase_a() { local config="$1" local cfg_dir="$RUN_ROOT/$config" mkdir -p "$cfg_dir" echo "=== [$config] Phase A ===" # Launch RUN_DIR="$cfg_dir" PORT="$PORT" GPU_ID="$GPU_ID" MODEL_PATH="$MODEL_PATH" \ bash "$HERE/launch/launch_${config}.sh" local rc=$? if [[ $rc == 42 ]]; then manifest "$config" "phase_a" "SKIP" "dependency missing" return 0 fi if [[ $rc != 0 ]]; then manifest "$config" "phase_a" "FAIL" "launch rc=$rc" return 0 fi # /metrics sampler in background "$PYTHON" "$HERE/metrics_sampler.py" \ --url "http://127.0.0.1:$PORT/metrics" \ --output "$cfg_dir/metrics_A.jsonl" \ --interval 1.0 & local sampler_pid=$! # bench loop "$PYTHON" "$HERE/bench_loop.py" \ --url "http://127.0.0.1:$PORT/v1/chat/completions" \ --model "$MODEL_PATH" \ --phase A \ --rates "$PHASE_A_RATES" \ --shape "4096,256" \ --duration "$DURATION" \ --min-completed "$MIN_COMPLETED" \ --warmup 10 \ --output-dir "$cfg_dir" local bench_rc=$? kill -9 "$sampler_pid" 2>/dev/null || true kill_vllm "$cfg_dir/.vllm.pid" [[ -f "$cfg_dir/.dummy.pid" ]] && kill -9 "$(cat "$cfg_dir/.dummy.pid")" 2>/dev/null if [[ $bench_rc != 0 ]]; then manifest "$config" "phase_a" "FAIL" "bench rc=$bench_rc" return 0 fi manifest "$config" "phase_a" "OK" "" } run_phase_b() { local config="$1" local cfg_dir="$RUN_ROOT/$config" mkdir -p "$cfg_dir" local ref_safe; ref_safe=$(jq -r '.ref_safe // empty' "$RUN_ROOT/aggregate.json" 2>/dev/null) if [[ -z "$ref_safe" || "$ref_safe" == "null" ]]; then echo "ref_safe not available; skipping Phase B for $config" manifest "$config" "phase_b" "SKIP" "ref_safe undefined" return 0 fi echo "=== [$config] Phase B (rate=$ref_safe) ===" RUN_DIR="$cfg_dir" PORT="$PORT" GPU_ID="$GPU_ID" MODEL_PATH="$MODEL_PATH" \ bash "$HERE/launch/launch_${config}.sh" local rc=$? if [[ $rc == 42 ]]; then manifest "$config" "phase_b" "SKIP" "dependency missing" return 0 fi if [[ $rc != 0 ]]; then manifest "$config" "phase_b" "FAIL" "launch rc=$rc" return 0 fi "$PYTHON" "$HERE/metrics_sampler.py" \ --url "http://127.0.0.1:$PORT/metrics" \ --output "$cfg_dir/metrics_B.jsonl" \ --interval 1.0 & local sampler_pid=$! "$PYTHON" "$HERE/bench_loop.py" \ --url "http://127.0.0.1:$PORT/v1/chat/completions" \ --model "$MODEL_PATH" \ --phase B \ --rate "$ref_safe" \ --shapes "$PHASE_B_SHAPES" \ --duration "$DURATION" \ --min-completed "$MIN_COMPLETED" \ --warmup 10 \ --output-dir "$cfg_dir" local bench_rc=$? kill -9 "$sampler_pid" 2>/dev/null || true kill_vllm "$cfg_dir/.vllm.pid" [[ -f "$cfg_dir/.dummy.pid" ]] && kill -9 "$(cat "$cfg_dir/.dummy.pid")" 2>/dev/null if [[ $bench_rc != 0 ]]; then manifest "$config" "phase_b" "FAIL" "bench rc=$bench_rc" return 0 fi manifest "$config" "phase_b" "OK" "" } # ── Stage 0 — pre-flight ──────────────────────────────────────────────── if [[ " $STAGES " == *" 0 "* ]]; then echo "=== Stage 0 — pre-flight ===" mkdir -p "$RUN_ROOT/preflight" pip freeze > "$RUN_ROOT/preflight/pip_freeze.txt" 2>&1 || true nvidia-smi -L > "$RUN_ROOT/preflight/nvidia.txt" 2>&1 || true # Apply step-timing patch unless SKIP_PATCH=1 if [[ "${SKIP_PATCH:-0}" != "1" ]]; then if "$PYTHON" "$HERE/patches/apply_step_timing.py" --apply > "$RUN_ROOT/preflight/patch.log" 2>&1; then echo "step_timing_available=true" > "$RUN_ROOT/preflight/patch_status.txt" else echo "step_timing_available=false (apply failed)" > "$RUN_ROOT/preflight/patch_status.txt" cat "$RUN_ROOT/preflight/patch.log" fi else echo "step_timing_available=false (SKIP_PATCH=1)" > "$RUN_ROOT/preflight/patch_status.txt" fi fi # ── Stage 1 — Phase A all configs ────────────────────────────────────── if [[ " $STAGES " == *" 1 "* ]]; then echo "=== Stage 1 — Phase A (all configs, randomized) ===" for cfg in $(shuffle_configs "${ALL_CONFIGS[@]}"); do run_phase_a "$cfg" done fi # ── Stage 2 — pick reference rates ───────────────────────────────────── if [[ " $STAGES " == *" 2 "* ]]; then echo "=== Stage 2 — pick reference rates ===" "$PYTHON" "$HERE/analyze.py" --root "$RUN_ROOT" fi # ── Stage 3 — Phase B all configs ────────────────────────────────────── if [[ " $STAGES " == *" 3 "* ]]; then echo "=== Stage 3 — Phase B (all configs, randomized) ===" for cfg in $(shuffle_configs "${ALL_CONFIGS[@]}"); do run_phase_b "$cfg" done fi # ── Stage 4 — analyze + plot + revert ────────────────────────────────── if [[ " $STAGES " == *" 4 "* ]]; then echo "=== Stage 4 — re-analyze with Phase B + plot + revert patch ===" "$PYTHON" "$HERE/analyze.py" --root "$RUN_ROOT" "$PYTHON" "$HERE/plot_connector_tax.py" --root "$RUN_ROOT" "$PYTHON" "$HERE/patches/apply_step_timing.py" --revert >> "$RUN_ROOT/preflight/patch.log" 2>&1 || true echo "Done. Results in $RUN_ROOT" fi