|
| 1 | +#!/usr/bin/env bash |
| 2 | +set -euo pipefail |
| 3 | +set -x |
| 4 | + |
| 5 | +# Agentic trace replay benchmark for Kimi-K2.7 FP4 on MI355X using vLLM. |
| 6 | +# |
| 7 | +# Variant of kimik2.7_fp4_mi355x.sh that supports TWO KV configs: |
| 8 | +# KV_OFFLOADING=none -> GPU KV only |
| 9 | +# KV_OFFLOADING=dram KV_OFFLOAD_BACKEND=lmcache -> LMCache MP server + connector |
| 10 | +# |
| 11 | +# Required env vars: |
| 12 | +# MODEL, TP, CONC, KV_OFFLOADING, TOTAL_CPU_DRAM_GB, RESULT_DIR, DURATION, EP_SIZE |
| 13 | + |
| 14 | + |
| 15 | +source "$(dirname "$0")/../../benchmark_lib.sh" |
| 16 | + |
| 17 | +check_env_vars MODEL TP CONC KV_OFFLOADING TOTAL_CPU_DRAM_GB RESULT_DIR DURATION EP_SIZE |
| 18 | + |
| 19 | +if [[ -n "${SLURM_JOB_ID:-}" ]]; then |
| 20 | + echo "JOB $SLURM_JOB_ID running on ${SLURMD_NODENAME:-unknown}" |
| 21 | +fi |
| 22 | + |
| 23 | +# ROCR/HIP visibility for vLLM 0.14+ |
| 24 | +if [ -n "${ROCR_VISIBLE_DEVICES:-}" ]; then |
| 25 | + export HIP_VISIBLE_DEVICES="$ROCR_VISIBLE_DEVICES" |
| 26 | +fi |
| 27 | + |
| 28 | +# `hf download` creates the target dir if missing and is itself idempotent. |
| 29 | +# When MODEL_PATH is unset (stand-alone runs), fall back to the HF_HUB_CACHE |
| 30 | +# Either way, MODEL_PATH is what the server is launched with. |
| 31 | +if [[ -n "${MODEL_PATH:-}" ]]; then |
| 32 | + if [[ ! -d "$MODEL_PATH" || -z "$(ls -A "$MODEL_PATH" 2>/dev/null)" ]]; then |
| 33 | + hf download "$MODEL" --local-dir "$MODEL_PATH" |
| 34 | + fi |
| 35 | +else |
| 36 | + hf download "$MODEL" |
| 37 | + export MODEL_PATH="$MODEL" |
| 38 | +fi |
| 39 | +rocm-smi || true |
| 40 | +amd-smi || true |
| 41 | + |
| 42 | +# ---- Resolve traces and install deps ---------------------------------------- |
| 43 | +resolve_trace_source |
| 44 | +install_agentic_deps |
| 45 | + |
| 46 | +# Install amd-quark for MXFP4 (manual install due to ROCm vLLM bug) |
| 47 | +pip install amd-quark |
| 48 | + |
| 49 | +# Disable AITER RMSNorm for TP < 8 due to accuracy issues |
| 50 | +if [ "${TP}" -lt 8 ]; then |
| 51 | + export VLLM_ROCM_USE_AITER_RMSNORM=0 |
| 52 | +fi |
| 53 | +# Workaround for MEC FW <177 RCCL memory reclaim issue |
| 54 | +version=$(rocm-smi --showfw 2>/dev/null | grep MEC | head -n 1 | awk '{print $NF}') |
| 55 | +if [[ "$version" == "" || ${version:-0} -lt 177 ]]; then |
| 56 | + export HSA_NO_SCRATCH_RECLAIM=1 |
| 57 | +fi |
| 58 | + |
| 59 | +export VLLM_ROCM_USE_AITER=1 |
| 60 | +export VLLM_ROCM_QUICK_REDUCE_QUANTIZATION=INT4 |
| 61 | +# Avoid intermittent symm_mem all-reduce rendezvous hang at engine init on |
| 62 | +# MI35x nodes (see KIMIK27_CONC64_LMCACHE_RUNBOOK error #4). |
| 63 | +export VLLM_ALLREDUCE_USE_SYMM_MEM="${VLLM_ALLREDUCE_USE_SYMM_MEM:-0}" |
| 64 | + |
| 65 | +# ---- Server config ---------------------------------------------------------- |
| 66 | +SERVER_LOG="$RESULT_DIR/server.log" |
| 67 | +LMCACHE_LOG="$RESULT_DIR/lmcache_server.log" |
| 68 | +mkdir -p "$RESULT_DIR" |
| 69 | + |
| 70 | +OFFLOAD_ARGS=() |
| 71 | +PREFIX_CACHE_ARGS=() |
| 72 | + |
| 73 | +# ---- LMCache config --------------------------------------------------------- |
| 74 | +LMCACHE_PID="" |
| 75 | + |
| 76 | +cleanup_lmcache_server() { |
| 77 | + if [[ -n "$LMCACHE_PID" ]] && kill -0 "$LMCACHE_PID" 2>/dev/null; then |
| 78 | + kill "$LMCACHE_PID" 2>/dev/null || true |
| 79 | + wait "$LMCACHE_PID" 2>/dev/null || true |
| 80 | + fi |
| 81 | +} |
| 82 | +trap cleanup_lmcache_server EXIT |
| 83 | + |
| 84 | +wait_for_lmcache_ready() { |
| 85 | + { set +x; } 2>/dev/null |
| 86 | + local attempts="${LMCACHE_READY_ATTEMPTS:-120}" |
| 87 | + local tail_pid="" |
| 88 | + |
| 89 | + while [ ! -f "$LMCACHE_LOG" ]; do |
| 90 | + if [[ -n "$LMCACHE_PID" ]] && ! kill -0 "$LMCACHE_PID" 2>/dev/null; then |
| 91 | + echo "LMCache server died before creating log file. Exiting." >&2 |
| 92 | + exit 1 |
| 93 | + fi |
| 94 | + sleep 1 |
| 95 | + done |
| 96 | + |
| 97 | + tail -f -n +1 "$LMCACHE_LOG" & |
| 98 | + tail_pid=$! |
| 99 | + |
| 100 | + for ((i = 1; i <= attempts; i++)); do |
| 101 | + if curl --output /dev/null --silent --fail "http://127.0.0.1:${LMCACHE_HTTP_PORT}/healthcheck"; then |
| 102 | + kill "$tail_pid" 2>/dev/null || true |
| 103 | + wait "$tail_pid" 2>/dev/null || true |
| 104 | + return 0 |
| 105 | + fi |
| 106 | + if [[ -n "$LMCACHE_PID" ]] && ! kill -0 "$LMCACHE_PID" 2>/dev/null; then |
| 107 | + echo "LMCache server died before becoming healthy. Log follows:" >&2 |
| 108 | + kill "$tail_pid" 2>/dev/null || true |
| 109 | + wait "$tail_pid" 2>/dev/null || true |
| 110 | + cat "$LMCACHE_LOG" >&2 || true |
| 111 | + exit 1 |
| 112 | + fi |
| 113 | + sleep 1 |
| 114 | + done |
| 115 | + |
| 116 | + echo "Timed out waiting for LMCache server healthcheck. Log follows:" >&2 |
| 117 | + kill "$tail_pid" 2>/dev/null || true |
| 118 | + wait "$tail_pid" 2>/dev/null || true |
| 119 | + cat "$LMCACHE_LOG" >&2 || true |
| 120 | + exit 1 |
| 121 | +} |
| 122 | + |
| 123 | +# Resolve the effective offload backend. When KV_OFFLOADING=none there is no |
| 124 | +# backend; when dram, KV_OFFLOAD_BACKEND selects native vs lmcache. |
| 125 | +if [[ "$KV_OFFLOADING" == "none" ]]; then |
| 126 | + OFFLOAD_MODE="none" |
| 127 | +else |
| 128 | + OFFLOAD_MODE="${KV_OFFLOAD_BACKEND:?KV_OFFLOAD_BACKEND required when KV_OFFLOADING=dram}" |
| 129 | +fi |
| 130 | + |
| 131 | +case "$OFFLOAD_MODE" in |
| 132 | + none) |
| 133 | + OFFLOAD_ARGS=(--no-enable-prefix-caching) |
| 134 | + ;; |
| 135 | + lmcache) |
| 136 | + unset VLLM_USE_SIMPLE_KV_OFFLOAD |
| 137 | + |
| 138 | + # Build LMCache against ROCm if the connector isn't already importable |
| 139 | + # (prebuilt kimi-lmcache images already ship it). Clone to a |
| 140 | + # container-local dir (NOT the bind-mounted /workspace) so the CI |
| 141 | + # checkout's `clean: true` never trips over root-owned build artifacts |
| 142 | + # on the next job. Pin a ref for reproducibility. |
| 143 | + if ! python3 -c "import lmcache.integration.vllm.lmcache_mp_connector" >/dev/null 2>&1; then |
| 144 | + LMCACHE_SRC_DIR="${LMCACHE_SRC_DIR:-/opt/lmcache-src}" |
| 145 | + LMCACHE_GIT_REF="${LMCACHE_GIT_REF:-aaf7c0d3}" |
| 146 | + rm -rf "$LMCACHE_SRC_DIR" |
| 147 | + git clone https://github.com/LMCache/LMCache.git "$LMCACHE_SRC_DIR" |
| 148 | + ( cd "$LMCACHE_SRC_DIR" |
| 149 | + git checkout "$LMCACHE_GIT_REF" |
| 150 | + pip install -r requirements/build.txt |
| 151 | + CXX=hipcc BUILD_WITH_HIP=1 pip install -e . --no-build-isolation ) |
| 152 | + python3 -c "import lmcache.integration.vllm.lmcache_mp_connector" >/dev/null |
| 153 | + fi |
| 154 | + |
| 155 | + LMCACHE_HOST="${LMCACHE_HOST:-127.0.0.1}" |
| 156 | + LMCACHE_PORT="${LMCACHE_PORT:-5555}" |
| 157 | + LMCACHE_HTTP_PORT="${LMCACHE_HTTP_PORT:-8080}" |
| 158 | + LMCACHE_CONNECT_HOST="${LMCACHE_CONNECT_HOST:-tcp://$LMCACHE_HOST}" |
| 159 | + # Let the external MP server own the whole CPU KV pool. The requested |
| 160 | + # budget is TOTAL_CPU_DRAM_GB, but LMCache's L1 is SHM-backed: if L1 > |
| 161 | + # /dev/shm free it silently disables SHM and falls back to the slow |
| 162 | + # pickle path (crashes at load — see kimik27 CI shm-overflow note). |
| 163 | + # Cap L1 to 90% of current /dev/shm free space so SHM stays enabled. |
| 164 | + SHM_FREE_GB=$(df -BG --output=avail /dev/shm 2>/dev/null | tail -1 | tr -dc '0-9') |
| 165 | + SHM_CAP_GB=$(( SHM_FREE_GB * 90 / 100 )) |
| 166 | + LMCACHE_L1_SIZE_GB="${LMCACHE_L1_SIZE_GB:-$TOTAL_CPU_DRAM_GB}" |
| 167 | + if [ -n "$SHM_CAP_GB" ] && [ "$SHM_CAP_GB" -gt 0 ] && [ "$LMCACHE_L1_SIZE_GB" -gt "$SHM_CAP_GB" ]; then |
| 168 | + echo "Capping LMCACHE_L1_SIZE_GB $LMCACHE_L1_SIZE_GB -> $SHM_CAP_GB to fit /dev/shm (${SHM_FREE_GB}G free)" |
| 169 | + LMCACHE_L1_SIZE_GB="$SHM_CAP_GB" |
| 170 | + fi |
| 171 | + LMCACHE_L1_INIT_SIZE_GB="${LMCACHE_L1_INIT_SIZE_GB:-20}" |
| 172 | + LMCACHE_L1_READ_TTL_SECONDS="${LMCACHE_L1_READ_TTL_SECONDS:-7200}" |
| 173 | + LMCACHE_CHUNK_SIZE="${LMCACHE_CHUNK_SIZE:-256}" |
| 174 | + LMCACHE_MAX_WORKERS="${LMCACHE_MAX_WORKERS:-$((TP * 2))}" |
| 175 | + export PYTHONHASHSEED="${PYTHONHASHSEED:-0}" |
| 176 | + export LMCACHE_BLOCKING_TIMEOUT_SECS=60 |
| 177 | + |
| 178 | + echo "Starting LMCache MP server..." |
| 179 | + LMCACHE_CMD=( |
| 180 | + lmcache server |
| 181 | + --host "$LMCACHE_HOST" |
| 182 | + --port "$LMCACHE_PORT" |
| 183 | + --http-host "$LMCACHE_HOST" |
| 184 | + --http-port "$LMCACHE_HTTP_PORT" |
| 185 | + --l1-size-gb "$LMCACHE_L1_SIZE_GB" |
| 186 | + --l1-init-size-gb "$LMCACHE_L1_INIT_SIZE_GB" |
| 187 | + --l1-read-ttl-seconds "$LMCACHE_L1_READ_TTL_SECONDS" |
| 188 | + --chunk-size "$LMCACHE_CHUNK_SIZE" |
| 189 | + --max-workers "$LMCACHE_MAX_WORKERS" |
| 190 | + --eviction-policy LRU |
| 191 | + ) |
| 192 | + printf '%q ' "${LMCACHE_CMD[@]}" > "$RESULT_DIR/lmcache_command.txt" |
| 193 | + printf '\n' >> "$RESULT_DIR/lmcache_command.txt" |
| 194 | + "${LMCACHE_CMD[@]}" > "$LMCACHE_LOG" 2>&1 & |
| 195 | + LMCACHE_PID=$! |
| 196 | + echo "LMCache server PID: $LMCACHE_PID" |
| 197 | + wait_for_lmcache_ready |
| 198 | + |
| 199 | + OFFLOAD_ARGS=( |
| 200 | + --kv-transfer-config |
| 201 | + "{\"kv_connector\":\"LMCacheMPConnector\",\"kv_connector_module_path\":\"lmcache.integration.vllm.lmcache_mp_connector\",\"kv_role\":\"kv_both\",\"kv_connector_extra_config\":{\"lmcache.mp.host\":\"$LMCACHE_CONNECT_HOST\",\"lmcache.mp.port\":$LMCACHE_PORT}}" |
| 202 | + ) |
| 203 | + ;; |
| 204 | + *) |
| 205 | + echo "Error: unsupported KV_OFFLOAD_BACKEND '$OFFLOAD_MODE' (expected: lmcache)" >&2 |
| 206 | + exit 1 |
| 207 | + ;; |
| 208 | +esac |
| 209 | + |
| 210 | +EP_ARGS=() |
| 211 | +if [ "$EP_SIZE" -gt 1 ]; then |
| 212 | + EP_ARGS=(--enable-expert-parallel) |
| 213 | +fi |
| 214 | + |
| 215 | +echo "Starting vllm server..." |
| 216 | +export PYTHONNOUSERSITE=1 |
| 217 | + |
| 218 | +{ set +x; } 2>/dev/null |
| 219 | +VLLM_CMD=( |
| 220 | + vllm serve "$MODEL_PATH" --served-model-name "$MODEL" |
| 221 | + --host 0.0.0.0 |
| 222 | + --port "$PORT" |
| 223 | + --tensor-parallel-size="$TP" |
| 224 | + "${EP_ARGS[@]}" |
| 225 | + --gpu-memory-utilization 0.90 |
| 226 | + --block-size=1 |
| 227 | + --trust-remote-code |
| 228 | + --max-num-seqs "$CONC" |
| 229 | + --mm-encoder-tp-mode data |
| 230 | + "${PREFIX_CACHE_ARGS[@]}" |
| 231 | + "${OFFLOAD_ARGS[@]}" |
| 232 | +) |
| 233 | +printf '%q ' "${VLLM_CMD[@]}" | tee "$RESULT_DIR/vllm_command.txt" |
| 234 | +printf '\n' | tee -a "$RESULT_DIR/vllm_command.txt" |
| 235 | +"${VLLM_CMD[@]}" > "$SERVER_LOG" 2>&1 & |
| 236 | +SERVER_PID=$! |
| 237 | +echo "Server PID: $SERVER_PID" |
| 238 | + |
| 239 | +wait_for_server_ready --port "$PORT" --server-log "$SERVER_LOG" --server-pid "$SERVER_PID" |
| 240 | + |
| 241 | +# ---- Run benchmark ---------------------------------------------------------- |
| 242 | +build_replay_cmd "$RESULT_DIR" |
| 243 | + |
| 244 | +run_agentic_replay_and_write_outputs "$RESULT_DIR" |
0 commit comments