Skip to content
Merged
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
8 changes: 4 additions & 4 deletions rigforge.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2123,7 +2123,7 @@ _xmrig_bench() { # <bin> <bench-size> <config|"">
# The MEDIAN clock over the window — robust to the brief low-clock dataset-init phase, which the raw
# min would mistake for thermal throttling (#62).
# shellcheck disable=SC2086 # $freqs is an intentional word-split list of samples
if [ -n "${BENCH_FREQ_FILE:-}" ]; then printf '%s' "$(_median $freqs)" >"$BENCH_FREQ_FILE"; fi
if [ -n "${BENCH_FREQ_FILE:-}" ]; then _median $freqs | awk '{printf "%d", $0+0}' >"$BENCH_FREQ_FILE"; fi
cat "$logf" "$out" 2>/dev/null
rm -f "$out" "$logf" "$tmpcfg" 2>/dev/null || true
}
Expand Down Expand Up @@ -2207,9 +2207,9 @@ _measure() { # <prefetch> <yield> <threads> <onegb> <priority> <hpjit> <cacheqos
[ -n "$s" ] || s=0
samples+=("$s")
bf=$(cat "$TUNE_TMP/freq" 2>/dev/null)
# _median emits a fractional kHz for an even sample count (e.g. 4627500.5); floor it to whole kHz
# so the integer guard below keeps the reading instead of dropping it — a dropped reading would
# leave min_freq_mhz null, silently disabling this candidate's #62 throttle check.
# The freq writer above already floors to whole kHz (awk's OFMT prints large medians in
# scientific notation, e.g. 4.6275e+06, which this floor would otherwise mangle into "4").
# This is now just a belt-and-suspenders trim of a stray fractional part.
bf=${bf%.*}
case "$bf" in '' | *[!0-9]*) ;; *) if [ -z "$minfk" ] || [ "$bf" -lt "$minfk" ]; then minfk="$bf"; fi ;; esac
wv=$(cat "$TUNE_TMP/watts" 2>/dev/null)
Expand Down
75 changes: 75 additions & 0 deletions tests/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4295,6 +4295,48 @@ out="$(confirm_run 'c=$(cat "$ACTR" 2>/dev/null||echo 0);c=$((c+1));echo $c>"$AC
assert_contains "confirm reverts a live regression (#64)" "$out" "Reverted:"
assert_eq "reverted -> previous (none) restored (#64)" "$([ -f "$OVR" ] && echo present || echo gone)" "gone"

# #266: unit-style — an even sample count whose middle pair sums to an ODD kHz total makes awk print the
# median in scientific notation (e.g. 4.6275e+06 for 4627000/4628001 kHz, real DVFS jitter between reads).
# The freq writer at rigforge.sh:2120 must floor that back to whole kHz, not leave it for the consumer's
# `.`-floor to truncate into a stray "4" that reads as 0 MHz and false-trips the #62 throttle guard.
echo "== unit: tune bench freq writer floors a sci-notation median to whole kHz (#266) =="
FQDONE="$SANDBOX/freq266.done"
export FQDONE
FQBIN="$SANDBOX/freq266-xmrig"
# The fake prints 'benchmark finished' once released: the bench poll loop greps for that line and breaks,
# so no extra clock sample sneaks in after the replayed ones (a just-exited fake can linger as a zombie
# that `kill -0` still sees, which would otherwise buy the loop one more iteration).
cat >"$FQBIN" <<'EOF'
#!/usr/bin/env bash
echo "speed 1000.0 H/s max 1000.0 H/s"
for _ in $(seq 1 500); do [ -f "$FQDONE" ] && break; sleep 0.02; done # bounded: never outlive the test
echo "benchmark finished"
EOF
chmod +x "$FQBIN"
freqwrite() { ( # <freq sample kHz>... -> BENCH_FREQ_FILE content, via the real _xmrig_bench freq-write path
source "$SCRIPT"
local vals=("$@") n=$# fqout="$SANDBOX/freq266.out" ctr="$SANDBOX/freq266.ctr"
rm -f "$FQDONE" "$fqout"
printf 0 >"$ctr"
# Deterministic clock reader: replay exactly the given samples, one per call, then signal the fake
# xmrig to finish and go silent — no reliance on real hardware jitter or wall-clock timing.
# The call counter lives in a FILE: each sample is read via $(...), a subshell where a plain shell
# variable increment would not persist. `return 0` keeps set -e happy on the non-final sample.
_cpu_eff_khz() {
local i
i=$(cat "$ctr")
if [ "$i" -ge "$n" ]; then return 0; fi # exhausted -> empty reading, which the sampler skips
printf '%s' "${vals[$i]}"
printf '%s' "$((i + 1))" >"$ctr"
if [ "$((i + 1))" -ge "$n" ]; then touch "$FQDONE"; fi
return 0
}
BENCH_FREQ_FILE="$fqout" _xmrig_bench "$FQBIN" 1M "" >/dev/null
cat "$fqout"
); }
assert_eq "bench freq median floors a sci-notation kHz average to whole kHz, not a stray digit (#266)" \
"$(freqwrite 4627000 4628001)" "4627500"

# #62: thermal-throttle rejection. A LOW clock source makes every candidate's window "throttled" — a
# faster-but-throttled candidate must NOT be adopted (its number reflects the throttle, not the config),
# and the throttle must be recorded in the log. With TUNE_MIN_FREQ_MHZ=0 the skip is disabled.
Expand Down Expand Up @@ -4329,6 +4371,39 @@ assert_eq "throttled faster candidate not adopted (#62)" "$(J "$OVR" '.randomx.s
out="$(throttle_run 0)"
assert_eq "TUNE_MIN_FREQ_MHZ=0 disables the throttle skip (#62 control)" "$(J "$OVR" '.randomx.scratchpad_prefetch_mode')" "2"

# #266: regression — a HEALTHY (~4.6 GHz) candidate whose clock jitters by 1 kHz between two reads gets a
# fractional kHz median (4627500.5) that awk printed as 4.6275e+06; the consumer's `.`-floor mangled that
# into "4" -> 0 MHz -> the #62 guard falsely flagged the candidate as throttled and refused to adopt it.
# scaling_cur_freq is a FIFO fed exactly one odd-sum pair per bench window (reads block until fed), so the
# window always sees an even sample count with a fractional median — no reliance on poll-loop timing.
echo "== black-box: tune bench freq median doesn't false-trip the #62 throttle guard (#266) =="
mkdir -p "$TN/cpu266/cpu0/cpufreq"
FF266="$TN/cpu266/cpu0/cpufreq/scaling_cur_freq"
DONE266="$TN/done266"
rm -f "$FF266" "$DONE266"
mkfifo "$FF266"
# Feeder: serve the pair, then release the fake xmrig so the bench window closes after exactly 2 samples.
(while :; do printf '4627000\n' >"$FF266" && printf '4628001\n' >"$FF266" && touch "$DONE266" || exit; done) &
FEED266=$!
cat >"$BD/xmrig" <<EOF
#!/usr/bin/env bash
echo "speed 1000.0 H/s max 1000.0 H/s"
for _ in \$(seq 1 500); do [ -f "$DONE266" ] && break; sleep 0.02; done # bounded wait
rm -f "$DONE266"
echo "benchmark finished" # breaks the bench poll loop BEFORE a zombie-pid extra iteration samples a 3rd clock
EOF
chmod +x "$BD/xmrig"
out="$(cd "$TN" && PATH="$STUBS:$PATH" CPU_SYSFS="$TN/cpu266" CPUFREQ_MAX="$TN/cpu_max" TUNE_MIN_FREQ_MHZ=4000 \
TUNE_ITERS=1 TUNE_SEEDS=auto TUNE_PREFETCH_MODES=1 TUNE_YIELDS=false TUNE_THREADS=-1 \
RIGFORGE_HOME="$PWD" bash "$SCRIPT" tune </dev/null 2>&1)"
rc=$?
kill "$FEED266" 2>/dev/null
wait "$FEED266" 2>/dev/null
rm -f "$FF266" "$DONE266"
assert_rc "healthy fractional-median tune exits 0 (#266)" "$rc" "0"
assert_absent "healthy candidate NOT flagged as throttled (#266)" "$out" "throttled to"
assert_eq "healthy candidate recorded as not throttled, eligible for adoption (#266)" "$(J "$TLOG" '.results[0].throttled')" "false"

# #265: _seed_wr / _seed_g must keep an explicit base-config false instead of jq `//` flipping it to
# the true default; with the key absent, the true default still applies.
echo "== unit: tune seeds keep explicit false (#265) =="
Expand Down