Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
235 changes: 235 additions & 0 deletions benchmarks/single_node/agentic/kimik2.7_fp4_mi355x.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
#!/usr/bin/env bash
set -euo pipefail
set -x

# Agentic trace replay benchmark for Kimi-K2.7 FP4 on MI355X using vLLM.
# KV_OFFLOADING=none -> GPU KV only
# KV_OFFLOADING=dram KV_OFFLOAD_BACKEND=lmcache -> LMCache MP server + connector
#
# Required env vars:
# MODEL, TP, CONC, KV_OFFLOADING, TOTAL_CPU_DRAM_GB, RESULT_DIR, DURATION, EP_SIZE


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

check_env_vars MODEL TP CONC KV_OFFLOADING TOTAL_CPU_DRAM_GB RESULT_DIR DURATION EP_SIZE

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

# ROCR/HIP visibility for vLLM 0.14+
if [ -n "${ROCR_VISIBLE_DEVICES:-}" ]; then
export HIP_VISIBLE_DEVICES="$ROCR_VISIBLE_DEVICES"
fi

if [[ -n "${MODEL_PATH:-}" ]]; then
if [[ ! -d "$MODEL_PATH" || -z "$(ls -A "$MODEL_PATH" 2>/dev/null)" ]]; then
hf download "$MODEL" --local-dir "$MODEL_PATH"
fi
else
hf download "$MODEL"
export MODEL_PATH="$MODEL"
fi
rocm-smi || true
amd-smi || true

# ---- Resolve traces and install deps ----------------------------------------
resolve_trace_source
install_agentic_deps

# Install amd-quark for MXFP4 (manual install due to ROCm vLLM bug)
pip install amd-quark

# Disable AITER RMSNorm for TP < 8 due to accuracy issues
if [ "${TP}" -lt 8 ]; then
export VLLM_ROCM_USE_AITER_RMSNORM=0
fi
# Workaround for MEC FW <177 RCCL memory reclaim issue
version=$(rocm-smi --showfw 2>/dev/null | grep MEC | head -n 1 | awk '{print $NF}')
if [[ "$version" == "" || ${version:-0} -lt 177 ]]; then
export HSA_NO_SCRATCH_RECLAIM=1
fi

export VLLM_ROCM_USE_AITER=1
export VLLM_ROCM_QUICK_REDUCE_QUANTIZATION=INT4
# Avoid intermittent symm_mem all-reduce rendezvous hang at engine init on
# MI35x nodes (see KIMIK27_CONC64_LMCACHE_RUNBOOK error #4).
export VLLM_ALLREDUCE_USE_SYMM_MEM="${VLLM_ALLREDUCE_USE_SYMM_MEM:-0}"

# ---- Server config ----------------------------------------------------------
SERVER_LOG="$RESULT_DIR/server.log"
LMCACHE_LOG="$RESULT_DIR/lmcache_server.log"
mkdir -p "$RESULT_DIR"

OFFLOAD_ARGS=()
PREFIX_CACHE_ARGS=()

# ---- LMCache config ---------------------------------------------------------
LMCACHE_PID=""

cleanup_lmcache_server() {
if [[ -n "$LMCACHE_PID" ]] && kill -0 "$LMCACHE_PID" 2>/dev/null; then
kill "$LMCACHE_PID" 2>/dev/null || true
wait "$LMCACHE_PID" 2>/dev/null || true
fi
}
trap cleanup_lmcache_server EXIT

wait_for_lmcache_ready() {
{ set +x; } 2>/dev/null
local attempts="${LMCACHE_READY_ATTEMPTS:-120}"
local tail_pid=""

while [ ! -f "$LMCACHE_LOG" ]; do
if [[ -n "$LMCACHE_PID" ]] && ! kill -0 "$LMCACHE_PID" 2>/dev/null; then
echo "LMCache server died before creating log file. Exiting." >&2
exit 1
fi
sleep 1
done

tail -f -n +1 "$LMCACHE_LOG" &
tail_pid=$!

for ((i = 1; i <= attempts; i++)); do
if curl --output /dev/null --silent --fail "http://127.0.0.1:${LMCACHE_HTTP_PORT}/healthcheck"; then
kill "$tail_pid" 2>/dev/null || true
wait "$tail_pid" 2>/dev/null || true
return 0
fi
if [[ -n "$LMCACHE_PID" ]] && ! kill -0 "$LMCACHE_PID" 2>/dev/null; then
echo "LMCache server died before becoming healthy. Log follows:" >&2
kill "$tail_pid" 2>/dev/null || true
wait "$tail_pid" 2>/dev/null || true
cat "$LMCACHE_LOG" >&2 || true
exit 1
fi
sleep 1
done

echo "Timed out waiting for LMCache server healthcheck. Log follows:" >&2
kill "$tail_pid" 2>/dev/null || true
wait "$tail_pid" 2>/dev/null || true
cat "$LMCACHE_LOG" >&2 || true
exit 1
}

if [[ "$KV_OFFLOADING" == "none" ]]; then
OFFLOAD_MODE="none"
else
OFFLOAD_MODE="${KV_OFFLOAD_BACKEND:?KV_OFFLOAD_BACKEND required when KV_OFFLOADING=dram}"
fi

case "$OFFLOAD_MODE" in
none)
# GPU-only KV baseline: keep on-GPU prefix caching ON (no DRAM offload)
# so it's apples-to-apples vs the lmcache cell.
PREFIX_CACHE_ARGS=(--enable-prefix-caching)
;;
lmcache)
unset VLLM_USE_SIMPLE_KV_OFFLOAD

# Build LMCache against ROCm if the connector isn't importable. Clone to
# a container-local dir (NOT bind-mounted /workspace) so the next job's
# checkout `clean: true` won't trip over root-owned build artifacts.
if ! python3 -c "import lmcache.integration.vllm.lmcache_mp_connector" >/dev/null 2>&1; then
LMCACHE_SRC_DIR="${LMCACHE_SRC_DIR:-/opt/lmcache-src}"
LMCACHE_GIT_REF="${LMCACHE_GIT_REF:-aaf7c0d3}"
rm -rf "$LMCACHE_SRC_DIR"
git clone https://github.com/LMCache/LMCache.git "$LMCACHE_SRC_DIR"
( cd "$LMCACHE_SRC_DIR"
git checkout "$LMCACHE_GIT_REF"
pip install -r requirements/build.txt
CXX=hipcc BUILD_WITH_HIP=1 pip install -e . --no-build-isolation )
python3 -c "import lmcache.integration.vllm.lmcache_mp_connector" >/dev/null
fi

LMCACHE_HOST="${LMCACHE_HOST:-127.0.0.1}"
LMCACHE_PORT="${LMCACHE_PORT:-5555}"
LMCACHE_HTTP_PORT="${LMCACHE_HTTP_PORT:-8080}"
LMCACHE_CONNECT_HOST="${LMCACHE_CONNECT_HOST:-tcp://$LMCACHE_HOST}"
# LMCache L1 is SHM-backed: if L1 > /dev/shm free it silently disables
# SHM and falls back to the pickle path (crashes at load). Cap L1 to 90%
# of /dev/shm free so SHM stays enabled.
SHM_FREE_GB=$(df -BG --output=avail /dev/shm 2>/dev/null | tail -1 | tr -dc '0-9')
SHM_CAP_GB=$(( SHM_FREE_GB * 90 / 100 ))
LMCACHE_L1_SIZE_GB="${LMCACHE_L1_SIZE_GB:-$TOTAL_CPU_DRAM_GB}"
Comment thread
hyukjlee marked this conversation as resolved.
if [ -n "$SHM_CAP_GB" ] && [ "$SHM_CAP_GB" -gt 0 ] && [ "$LMCACHE_L1_SIZE_GB" -gt "$SHM_CAP_GB" ]; then
echo "Capping LMCACHE_L1_SIZE_GB $LMCACHE_L1_SIZE_GB -> $SHM_CAP_GB to fit /dev/shm (${SHM_FREE_GB}G free)"
LMCACHE_L1_SIZE_GB="$SHM_CAP_GB"
fi
LMCACHE_L1_INIT_SIZE_GB="${LMCACHE_L1_INIT_SIZE_GB:-20}"
LMCACHE_L1_READ_TTL_SECONDS="${LMCACHE_L1_READ_TTL_SECONDS:-7200}"
LMCACHE_CHUNK_SIZE="${LMCACHE_CHUNK_SIZE:-256}"
LMCACHE_MAX_WORKERS="${LMCACHE_MAX_WORKERS:-$((TP * 2))}"
export PYTHONHASHSEED="${PYTHONHASHSEED:-0}"
export LMCACHE_BLOCKING_TIMEOUT_SECS=60

echo "Starting LMCache MP server..."
LMCACHE_CMD=(
lmcache server
--host "$LMCACHE_HOST"
--port "$LMCACHE_PORT"
--http-host "$LMCACHE_HOST"
--http-port "$LMCACHE_HTTP_PORT"
--l1-size-gb "$LMCACHE_L1_SIZE_GB"
--l1-init-size-gb "$LMCACHE_L1_INIT_SIZE_GB"
--l1-read-ttl-seconds "$LMCACHE_L1_READ_TTL_SECONDS"
--chunk-size "$LMCACHE_CHUNK_SIZE"
--max-workers "$LMCACHE_MAX_WORKERS"
--eviction-policy LRU
)
printf '%q ' "${LMCACHE_CMD[@]}" > "$RESULT_DIR/lmcache_command.txt"
printf '\n' >> "$RESULT_DIR/lmcache_command.txt"
"${LMCACHE_CMD[@]}" > "$LMCACHE_LOG" 2>&1 &
LMCACHE_PID=$!
echo "LMCache server PID: $LMCACHE_PID"
wait_for_lmcache_ready

OFFLOAD_ARGS=(
--kv-transfer-config
"{\"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}}"
)
;;
*)
echo "Error: unsupported KV_OFFLOAD_BACKEND '$OFFLOAD_MODE' (expected: lmcache)" >&2
exit 1
;;
esac

EP_ARGS=()
if [ "$EP_SIZE" -gt 1 ]; then
EP_ARGS=(--enable-expert-parallel)
fi

echo "Starting vllm server..."
export PYTHONNOUSERSITE=1

{ set +x; } 2>/dev/null
VLLM_CMD=(
vllm serve "$MODEL_PATH" --served-model-name "$MODEL"
--host 0.0.0.0
--port "$PORT"
--tensor-parallel-size="$TP"
"${EP_ARGS[@]}"
--gpu-memory-utilization 0.90
--block-size=1
--trust-remote-code
--max-num-seqs "$CONC"
--mm-encoder-tp-mode data
"${PREFIX_CACHE_ARGS[@]}"
"${OFFLOAD_ARGS[@]}"
)
printf '%q ' "${VLLM_CMD[@]}" | tee "$RESULT_DIR/vllm_command.txt"
printf '\n' | tee -a "$RESULT_DIR/vllm_command.txt"
"${VLLM_CMD[@]}" > "$SERVER_LOG" 2>&1 &
SERVER_PID=$!
echo "Server PID: $SERVER_PID"

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

# ---- Run benchmark ----------------------------------------------------------
build_replay_cmd "$RESULT_DIR"

run_agentic_replay_and_write_outputs "$RESULT_DIR"
16 changes: 16 additions & 0 deletions configs/amd-master.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -808,6 +808,22 @@ kimik2.5-fp4-mi355x-vllm-agentic:
- { tp: 4, kv-offloading: none, conc-list: [16, 24, 32, 40] }
- { tp: 4, kv-offloading: dram, kv-offload-backend: native, conc-list: [16, 24, 32, 40] }

kimik2.7-fp4-mi355x-vllm-agentic:
image: vllm/vllm-openai-rocm:v0.24.0
model: amd/Kimi-K2.7-Code-MXFP4
model-prefix: kimik2.7
runner: cluster:mi355x-amds
precision: fp4
framework: vllm
multinode: false
scenarios:
agentic-coding:
- dram-utilization: 0.80
search-space:
- { tp: 4, kv-offloading: none, conc-list: [1, 4, 8] }
- { tp: 4, kv-offloading: dram, kv-offload-backend: lmcache, conc-list: [16, 32] }


kimik2.5-fp4-mi355x-atom:
image: rocm/atom:rocm7.2.3_ubuntu24.04_py3.12_pytorch_release_2.10.0_atom20260511
model: amd/Kimi-K2.5-MXFP4
Expand Down
8 changes: 8 additions & 0 deletions perf-changelog.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4658,6 +4658,14 @@
- "Recipes sourced from NVIDIA/srt-slurm branch sa-submission-q2-2026 (gb300_nvfp4 MTP recipes)"
pr-link: https://github.com/SemiAnalysisAI/InferenceX/pull/1799

- config-keys:
- kimik2.7-fp4-mi355x-vllm-agentic
description:
- "Add Kimi-K2.7-Code-MXFP4 agentic-coding config on MI355X (runner: cluster:mi355x-amds) via vLLM v0.24.0, TP4"
- "Sweep KV-offloading none (conc [1,4,8]) vs dram/LMCache (conc [16,32]); LMCache MP server + LMCacheMPConnector, dram-utilization 0.80"
- "Recipe benchmarks/single_node/agentic/kimik2.7_fp4_mi355x.sh clones LMCache to container-local /opt/lmcache-src (pinned) so CI checkout never trips over root-owned build artifacts"
pr-link: https://github.com/SemiAnalysisAI/InferenceX/pull/2126

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 The new changelog entry's pr-link is literally .../pull/PLACEHOLDER (perf-changelog.yaml:4644). utils/validate_perf_changelog.py only accepts the canonical link (.../pull/2126 for this PR) or the XXX sentinel — PLACEHOLDER is neither, so the changelog-gate CI step in .github/workflows/run-sweep.yml will raise ChangelogValidationError and fail this PR. Fix: replace PLACEHOLDER with 2126 (or XXX) before merge.

Extended reasoning...

The new changelog entry ends with pr-link: https://github.com/SemiAnalysisAI/InferenceX/pull/PLACEHOLDER (perf-changelog.yaml:4644). Every surrounding entry uses a real PR number (2113, 2114, 2115), so this is unambiguously a leftover placeholder that was never rewritten with the real PR id.

This is not just cosmetic. utils/validate_perf_changelog.py defines the only accepted placeholder set:

PR_LINK_PLACEHOLDERS = {"XXX", "https://github.com/SemiAnalysisAI/InferenceX/pull/XXX"}

and validate_added_pr_link (line 144) enforces link in PR_LINK_PLACEHOLDERS or link == expected where expected == f"https://github.com/SemiAnalysisAI/InferenceX/pull/{pr_number}". The literal string "PLACEHOLDER" (or the full .../pull/PLACEHOLDER URL) is in neither set, so the validator will raise ChangelogValidationError on this PR.

That validator is wired into CI via .github/workflows/run-sweep.yml:102 (python3 utils/validate_perf_changelog.py), so the changelog-gate step will actively fail this PR until the link is fixed. utils/prepare_perf_changelog_merge.py performs the same check on the reuse-merge path, so even if CI were bypassed, the merge-with-reuse tooling would refuse the entry.

Step-by-step proof:

  1. On PR-2126 CI, the changelog gate invokes validate_perf_changelog.py with --pr-number 2126.
  2. It parses head perf-changelog.yaml, finds the newly appended kimik2.7-fp4-mi355x-vllm-agentic entry.
  3. It calls validate_added_pr_link("https://github.com/SemiAnalysisAI/InferenceX/pull/PLACEHOLDER", 2126).
  4. expected = "https://github.com/SemiAnalysisAI/InferenceX/pull/2126". The link is neither expected nor a member of PR_LINK_PLACEHOLDERS (only "XXX" / ".../pull/XXX").
  5. raise ChangelogValidationError(...) → the CI job exits non-zero → the PR check goes red.
  6. If it somehow merged, the on-site changelog would contain a permanently dead hyperlink pointing at /pull/PLACEHOLDER, breaking the pr-link traceability contract every surrounding entry follows.

Fix: change the value at perf-changelog.yaml:4644 to https://github.com/SemiAnalysisAI/InferenceX/pull/2126 (or the accepted XXX placeholder if you'd rather punt until squash-merge time). One-token change.


- config-keys:
- dsv4-fp4-mi355x-sglang-mtp
description:
Expand Down