135 lines
4.9 KiB
Bash
Executable File
135 lines
4.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# mi50_power_context_sweep.sh
|
|
#
|
|
# Two-axis sweep for dual MI50s: power limit x context size.
|
|
# For each (power, context) pair it runs llama-bench on gpt-oss and logs
|
|
# prefill (pp) and decode (tg) tok/s plus edge temp and actual draw.
|
|
#
|
|
# The question this answers: the power level that is "free" at small
|
|
# context (decode is HBM-bandwidth bound, core clock barely matters) may
|
|
# NOT be free at large context, where prefill is compute-heavy and a low
|
|
# core clock could bite. This grid shows where, if anywhere, that happens.
|
|
#
|
|
# Set MODEL below. Assumes llama-bench (llama.cpp ROCm build) in PATH.
|
|
# Run in tmux; a full grid takes a while (big model reloads per context).
|
|
|
|
set -uo pipefail # not -e: a single failed run should not kill the sweep
|
|
|
|
# ------------------------------- config -------------------------------
|
|
MODEL="${MODEL:-$HOME/.cache/llama.cpp/openai_gpt-oss-120b-Q4_K_M/openai_gpt-oss-120b-Q4_K_M-00001-of-00002.gguf}"
|
|
NGL=999
|
|
GPUS=(0 1)
|
|
|
|
# power levels in watts (stock cap 300, sweet spot expected 180-225)
|
|
POWER_LEVELS=(225 200 180 160)
|
|
|
|
# context sizes: (prompt_tokens gen_tokens) pairs.
|
|
# small, medium, large, xlarge. gen held at 128 so decode is comparable;
|
|
# prompt scales to exercise prefill compute at each context.
|
|
CTX_PROMPT=(512 4096 16384 32768)
|
|
CTX_GEN=128
|
|
|
|
COOLDOWN=20 # seconds between runs to normalize temps
|
|
REPS=2 # runs per cell, averaged (reduces noise)
|
|
LOGFILE="mi50_pxc_sweep_$(date +%Y%m%d_%H%M%S).csv"
|
|
# ----------------------------------------------------------------------
|
|
|
|
command -v rocm-smi >/dev/null || { echo "rocm-smi not found"; exit 1; }
|
|
command -v llama-bench >/dev/null || { echo "llama-bench not found in PATH"; exit 1; }
|
|
[[ -f "$MODEL" ]] || { echo "Model not found: $MODEL"; exit 1; }
|
|
|
|
echo "power_w,ctx_prompt,gen_tokens,pp_tok_s,tg_tok_s,edge_temp_c,draw_w" > "$LOGFILE"
|
|
echo "Logging to $LOGFILE"
|
|
echo "Model: $MODEL"
|
|
|
|
restore_power() {
|
|
echo ""
|
|
echo "Restoring stock power limit on all GPUs..."
|
|
for g in "${GPUS[@]}"; do
|
|
rocm-smi -d "$g" --resetpoweroverdrive >/dev/null 2>&1 || true
|
|
done
|
|
}
|
|
trap restore_power EXIT INT TERM
|
|
|
|
# pull a float from arbitrary rocm-smi output, first match
|
|
grab_float() { grep -oE '[0-9]+\.[0-9]+' | head -1; }
|
|
|
|
# extract the tok/s value (last numeric field) from a llama-bench csv row
|
|
# matching a given test tag (e.g. pp512 or tg128)
|
|
extract_toks() {
|
|
local out="$1" tag="$2"
|
|
echo "$out" \
|
|
| grep -iE "[\",]${tag}[\",]" \
|
|
| tail -1 \
|
|
| awk -F',' '{gsub(/"/,"",$NF); print $NF}'
|
|
}
|
|
|
|
sample_temp() {
|
|
rocm-smi -d 0 --showtemp 2>/dev/null | grep -i edge | grab_float
|
|
}
|
|
sample_draw() {
|
|
rocm-smi -d 0 --showpower 2>/dev/null | grab_float
|
|
}
|
|
|
|
total_cells=$(( ${#POWER_LEVELS[@]} * ${#CTX_PROMPT[@]} ))
|
|
cell=0
|
|
|
|
for pw in "${POWER_LEVELS[@]}"; do
|
|
echo ""
|
|
echo "############ POWER = ${pw}W ############"
|
|
for g in "${GPUS[@]}"; do
|
|
rocm-smi -d "$g" --setpoweroverdrive "$pw" >/dev/null 2>&1 \
|
|
|| echo " warn: failed to set ${pw}W on GPU $g"
|
|
done
|
|
sleep 3
|
|
|
|
for pp in "${CTX_PROMPT[@]}"; do
|
|
cell=$((cell+1))
|
|
echo ""
|
|
echo "--- [$cell/$total_cells] power=${pw}W prompt=${pp} gen=${CTX_GEN} ---"
|
|
|
|
pp_sum=0; tg_sum=0; ok=0
|
|
for r in $(seq 1 "$REPS"); do
|
|
OUT=$(llama-bench -m "$MODEL" -ngl "$NGL" \
|
|
-p "$pp" -n "$CTX_GEN" -o csv 2>/dev/null)
|
|
|
|
PP=$(extract_toks "$OUT" "pp${pp}")
|
|
TG=$(extract_toks "$OUT" "tg${CTX_GEN}")
|
|
# fallback: positional (rows 2 and 3) if tag match missed
|
|
[[ -z "${PP:-}" ]] && PP=$(echo "$OUT" | awk -F',' 'NR==2{gsub(/"/,"",$NF);print $NF}')
|
|
[[ -z "${TG:-}" ]] && TG=$(echo "$OUT" | awk -F',' 'NR==3{gsub(/"/,"",$NF);print $NF}')
|
|
|
|
if [[ -n "${PP:-}" && -n "${TG:-}" ]]; then
|
|
pp_sum=$(awk -v a="$pp_sum" -v b="$PP" 'BEGIN{print a+b}')
|
|
tg_sum=$(awk -v a="$tg_sum" -v b="$TG" 'BEGIN{print a+b}')
|
|
ok=$((ok+1))
|
|
else
|
|
echo " rep $r: parse miss (pp=${PP:-NA} tg=${TG:-NA})"
|
|
fi
|
|
sleep "$COOLDOWN"
|
|
done
|
|
|
|
if [[ "$ok" -gt 0 ]]; then
|
|
PP_AVG=$(awk -v s="$pp_sum" -v n="$ok" 'BEGIN{printf "%.2f", s/n}')
|
|
TG_AVG=$(awk -v s="$tg_sum" -v n="$ok" 'BEGIN{printf "%.2f", s/n}')
|
|
else
|
|
PP_AVG=NA; TG_AVG=NA
|
|
fi
|
|
|
|
TEMP=$(sample_temp); DRAW=$(sample_draw)
|
|
echo " => pp: ${PP_AVG} tok/s | tg: ${TG_AVG} tok/s | temp: ${TEMP:-NA}C | draw: ${DRAW:-NA}W"
|
|
echo "${pw},${pp},${CTX_GEN},${PP_AVG},${TG_AVG},${TEMP:-NA},${DRAW:-NA}" >> "$LOGFILE"
|
|
done
|
|
done
|
|
|
|
echo ""
|
|
echo "======================================================================"
|
|
echo "Sweep complete. Raw results in $LOGFILE"
|
|
echo ""
|
|
column -t -s',' "$LOGFILE"
|
|
|
|
echo ""
|
|
echo "Quick read: for each context size, compare tg across power levels."
|
|
echo "If tg holds flat as power drops, that context is bandwidth-bound and"
|
|
echo "low power is free. If tg falls off at low power for large context,"
|
|
echo "that is prefill compute starvation -- keep power higher for that regime." |