-
Notifications
You must be signed in to change notification settings - Fork 294
[PowerX] isolate AMD monitor windows and worker lifecycle / 隔离 AMD 监控窗口与工作节点生命周期 #3055
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
c304072
ba0275c
f691e7e
ec50eb1
07d1cf1
b230e70
4a0e8c8
880f115
be06dfb
654c33b
1422f53
d4a258a
bc8684e
54a64c9
d2575bb
d141e71
e3e473f
8b49ebe
df08a7f
8408779
f5c749d
6ed8f8b
8c2f834
85384dd
609cedc
44a4dc1
8fa4905
5a828ee
5f675a5
3ef51c7
8e95cd0
bf52da7
51c278a
ebaba2a
870a595
b2852be
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -52,6 +52,16 @@ profile_folder="${log_path}/${ENGINE}_isl_${chosen_isl}_osl_${chosen_osl}" | |
| mkdir -p "$profile_folder" | ||
|
|
||
| source "$(dirname "$0")/../../benchmark_lib.sh" | ||
| source "$(dirname "$0")/power.sh" | ||
| power_required=0 | ||
| case "${REQUIRE_POWER:-0}" in | ||
| 1|true|TRUE|yes|YES) power_required=1 ;; | ||
| esac | ||
| if ! wait_amd_multinode_power ready; then | ||
| [[ "$power_required" == 0 ]] || exit 1 | ||
| echo 'PowerX: continuing without ready optional telemetry' >&2 | ||
| fi | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ready failure leaves collectors runningMedium Severity When Additional Locations (2)Reviewed by Cursor Bugbot for commit ebaba2a. Configure here. |
||
| benchmark_exit_code=0 | ||
|
|
||
| REPO_ROOT="$(cd "$(dirname "$0")/../../.." && pwd)" | ||
|
|
||
|
|
@@ -101,6 +111,7 @@ for max_concurrency in "${chosen_concurrencies[@]}"; do | |
| fi | ||
| fi | ||
|
|
||
| point_exit_code=0 | ||
| run_benchmark_serving \ | ||
| --bench-serving-dir "$REPO_ROOT" \ | ||
| --model "$BENCH_MODEL" \ | ||
|
|
@@ -113,7 +124,8 @@ for max_concurrency in "${chosen_concurrencies[@]}"; do | |
| --max-concurrency "$max_concurrency" \ | ||
| --result-filename "$export_file" \ | ||
| --result-dir /workspace/ \ | ||
| $extra_flags | ||
| $extra_flags || point_exit_code=$? | ||
| if [[ "$point_exit_code" != 0 ]]; then benchmark_exit_code=$point_exit_code; break; fi | ||
|
|
||
| echo "-----------------------------------------" | ||
|
|
||
|
|
@@ -123,3 +135,10 @@ for max_concurrency in "${chosen_concurrencies[@]}"; do | |
| sleep 10 | ||
| fi | ||
| done | ||
|
|
||
| # Stop every node while all prefill/decode servers are still alive. | ||
| if ! wait_amd_multinode_power done; then | ||
| echo 'PowerX: collector completion failed' >&2 | ||
| if [[ "$power_required" == 1 && "$benchmark_exit_code" == 0 ]]; then benchmark_exit_code=1; fi | ||
| fi | ||
| exit "$benchmark_exit_code" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -237,8 +237,9 @@ fi | |
| # Node Selection | ||
| # ============================================================================= | ||
|
|
||
| NUM_NODES=$((xP + yD)) | ||
| echo "NUM_NODES: $NUM_NODES (xP=$xP + yD=$yD)" | ||
| # Workers can span multiple physical nodes. Preserve the submit-time count. | ||
| NUM_NODES="${NUM_NODES:-$(( ((PREFILL_TP_SIZE + GPUS_PER_NODE - 1) / GPUS_PER_NODE) * xP + ((DECODE_TP_SIZE + GPUS_PER_NODE - 1) / GPUS_PER_NODE) * yD ))}" | ||
| echo "NUM_NODES: $NUM_NODES (prefill workers=$xP, decode workers=$yD)" | ||
|
|
||
| FULL_NODELIST=$(scontrol show hostnames "$SLURM_JOB_NODELIST") | ||
| SELECTED_NODES=$(echo "$FULL_NODELIST" | head -n $NUM_NODES) | ||
|
|
@@ -318,6 +319,10 @@ export DRY_RUN="${DRY_RUN:-0}" | |
| export BENCHMARK_LOGS_DIR="${BENCHMARK_LOGS_DIR:-$(pwd)/benchmark_logs}" | ||
| export KEEP_CONTAINERS="${KEEP_CONTAINERS:-0}" | ||
| export ENGINE=$ENGINE | ||
| export POWERX_HOST_UID=$(id -u) | ||
| export POWERX_HOST_GID=$(id -g) | ||
| export POWERX_COLLECTOR_REVISION=$(git -C "$DI_REPO_DIR" rev-parse HEAD) | ||
| mkdir -p "$BENCHMARK_LOGS_DIR/power-control-${SLURM_JOB_ID}" | ||
|
|
||
| # Eval-related env vars (threaded from submit.sh) | ||
| export RUN_EVAL="${RUN_EVAL:-false}" | ||
|
|
@@ -377,6 +382,16 @@ else | |
| echo "[WARN] $RDMA_CHECK_SCRIPT not found; skipping RDMA QoS/DCQCN pre-flight check" | ||
| fi | ||
|
|
||
| stage_native_power() { | ||
| srun --overlap --nodelist="$SELECTED_NODELIST_SRUN" --ntasks="$NUM_NODES" bash -c ' | ||
| src="/tmp/slurm_job-${SLURM_JOB_ID}/native_power/node-${SLURM_PROCID}" | ||
| if [[ -d "$src" ]]; then | ||
| mkdir -p "$BENCHMARK_LOGS_DIR/native_power" | ||
| cp -r "$src" "$BENCHMARK_LOGS_DIR/native_power/" | ||
| fi | ||
| ' | ||
| } | ||
|
|
||
| cleanup() { | ||
| echo "[${SLURM_JOB_ID}] termination received on $(hostname); cleaning up container + stale logs..." | ||
| # Backstop: on scancel/timeout/step-hang the foreground `exec docker run` | ||
|
|
@@ -386,11 +401,13 @@ cleanup() { | |
| # other users' containers. (Ported from InferenceY 51ebfa88.) | ||
| srun --nodelist="$SELECTED_NODELIST_SRUN" \ | ||
| bash -c 'eval "$DOCKER_CMD_DETECT"; $DOCKER_CMD rm -f '"$DOCKER_CONT_NAME"' 2>/dev/null || true' 2>/dev/null || true | ||
| stage_native_power || true | ||
| rm -rf ${SLURM_SUBMIT_DIR}/logs 2>/dev/null || true | ||
| echo "[${SLURM_JOB_ID}] cleanup done." | ||
| } | ||
|
|
||
| trap cleanup INT TERM HUP | ||
| trap 'cleanup; exit 130' INT | ||
| trap 'cleanup; exit 143' TERM HUP | ||
|
|
||
| # Force NFS cache refresh on all nodes | ||
| echo "Refreshing NFS caches on all nodes..." | ||
|
|
@@ -412,6 +429,12 @@ DOCKER_ENV_COMMON=( | |
| -e SLURM_JOB_ID=\$SLURM_JOB_ID | ||
| -e SLURM_JOB_NODELIST=\$SLURM_JOB_NODELIST | ||
| -e NNODES=\$NNODES | ||
| -e REQUIRE_POWER=\${REQUIRE_POWER:-0} | ||
| -e POWERX_HOST_UID=\$POWERX_HOST_UID | ||
| -e POWERX_HOST_GID=\$POWERX_HOST_GID | ||
| -e POWERX_COLLECTOR_REVISION=\$POWERX_COLLECTOR_REVISION | ||
| -e POWERX_NODE_NAME=\$POWERX_NODE_NAME | ||
| -e POWERX_CLOCK_SYNCHRONIZED=\$POWERX_CLOCK_SYNCHRONIZED | ||
|
cursor[bot] marked this conversation as resolved.
|
||
| -e NODE_RANK=\$SLURM_PROCID | ||
| -e NODE0_ADDR=\$NODE0_ADDR | ||
| -e MODEL_DIR=/models | ||
|
|
@@ -601,6 +624,10 @@ set -euo pipefail | |
|
|
||
| echo \"Rank \$SLURM_PROCID on \$(hostname)\" | ||
|
|
||
| # Capture the host's synchronization state, not the container's missing D-Bus. | ||
| export POWERX_NODE_NAME=\$(hostname) | ||
| export POWERX_CLOCK_SYNCHRONIZED=\$(timedatectl show -p NTPSynchronized --value 2>/dev/null || true) | ||
|
|
||
| # Per-node docker privilege detection | ||
| eval \"\$DOCKER_CMD_DETECT\" | ||
| echo \"[docker-detect] rank \$SLURM_PROCID: DOCKER_CMD=\$DOCKER_CMD\" | ||
|
|
@@ -784,6 +811,17 @@ echo \"[rank 0] Main container exited (rc=\$DOCKER_EXIT_CODE). Stopping vllm-rou | |
| exit \$DOCKER_EXIT_CODE | ||
| " | ||
|
|
||
| BENCHMARK_STEP_RC=$? | ||
| # Each host copies its own node-local, root-created artifacts as the runner user. | ||
| # No raw telemetry is written as root into the shared checkout. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔴 Extended reasoning...job.slurm line 813: Verification: normal. job.slurm line 813 captures the real serving exit code ( |
||
| if ! stage_native_power; then | ||
| echo 'PowerX: failed to stage native telemetry' >&2 | ||
| case "${REQUIRE_POWER:-0}" in | ||
| 1|true|TRUE|yes|YES) | ||
| if [[ "$BENCHMARK_STEP_RC" == 0 ]]; then BENCHMARK_STEP_RC=1; fi ;; | ||
| esac | ||
| fi | ||
|
|
||
| if [[ "${KEEP_CONTAINERS}" != "1" ]]; then | ||
| srun --nodelist="$SELECTED_NODELIST_SRUN" bash -c 'eval "$DOCKER_CMD_DETECT"; $DOCKER_CMD rm -f '"$DOCKER_CONT_NAME"' '"$CLIENT_CONT_NAME"' 2>/dev/null || true' | ||
|
|
||
|
|
@@ -794,3 +832,5 @@ if [[ "${KEEP_CONTAINERS}" != "1" ]]; then | |
| ' | ||
| fi | ||
| fi | ||
|
|
||
| exit "$BENCHMARK_STEP_RC" | ||


There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 (optional) For AMD amd-smi streams that never produce a usable numeric epoch-second sample (e.g. an amd-smi build stamping ISO timestamps, or unexpected CSV column names), _wait_for_amd_stop_coverage does not fall back to a quick wait as docs/results-and-ingestion.md claims ("Streams without usable epoch timestamps use the legacy fixed tail wait"); it instead polls once per second until the full AMD_MONITOR_STOP_TIMEOUT_S deadline (default 30s) before giving up, versus the old fixed ~(interval+2)s (~3s) sleep. This adds ~30s of stall to every stop_gpu_monitor call in that environment, a real wall-clock regression vs base. Fix: detect the 'stream never produced any usable tick' case early and bail with a short wait instead of consuming the full timeout, matching the documented fallback.
Extended reasoning...
In _wait_for_amd_stop_coverage (benchmarks/benchmark_lib.sh:369-398), the while loop calls _amd_monitor_min_covered_tick each second; when the CSV never contains a row matching the numeric-epoch/positive-power/gpu-column pattern (e.g. amd-smi emits ISO8601 timestamps, or a different column name than the regex list),
coveredis always empty, so the-n "$covered"branch never fires. The monitor process is still alive (GPU_MONITOR_PID running), so the early-exit-on-death branch also never fires. The loop therefore runs untildate +%s -ge deadline(target+timeout_s, i.e. ~31s after entry with the default 30s timeout), only then warning and returning 0. The old code (deleted by this diff) just didsleep $((interval+2)), typically ~3s. docs/results-and-ingestion.md explicitly documents a 'legacy fixed tail wait' fallback for this exact case, but no such code path exists post-diff — every stop_gpu_monitor call in an unsupported-timestamp AMD environment now stalls ~10x longer than before, multiplied across every concurrency point of every AMD sweep run.Verification: nit. Factually correct but bounded-slowdown, not a functional failure. _wait_for_amd_stop_coverage (benchmarks/benchmark_lib.sh:384-400) has no legacy-fixed-tail-wait fallback: when _amd_monitor_min_covered_tick always returns empty (stream with no usable numeric-epoch row), line 388 (-n "$covered") never fires, line 391 death branch never fires while the awk sink GPU_MONITOR_PID stays alive,…