Skip to content
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

perf: EXC-1818: Add scripts to run all embedder and EE benchmarks #3054

Merged
merged 7 commits into from
Dec 12, 2024
64 changes: 64 additions & 0 deletions rs/execution_environment/benches/run-all-benchmarks.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#!/usr/bin/env bash
set -ue
##
## Top-level script to run all execution and embedder benchmarks.
## Usage:
## ./rs/execution_environment/benches/run-all-benchmarks.sh | tee summary.txt
##
## The best (minimum) results are located in the `*.min`` files in the current directory.
## These should be manually copied to `rs/execution_environment/benches/baseline/`.
## A summary of the results is printed to the standard output.
##

printf "%-12s := %s\n" \
"REPEAT" "${REPEAT:=9}" >&2

RUN_BENCHMARK="${0%/*}/run-benchmark.sh"
[ -x "${RUN_BENCHMARK}" ] || (echo "Error accessing script: ${RUN_BENCHMARK}" >&2 && exit 1)
SUMMARIZE_RESULTS="${0%/*}/summarize-results.sh"
[ -x "${SUMMARIZE_RESULTS}" ] || (echo "Error accessing script: ${SUMMARIZE_RESULTS}" >&2 && exit 1)

run() {
local i="${1}"
local name="${2}"
local bench="${3}"
# File with best (min) results.
local min_file="${4}"
maksymar marked this conversation as resolved.
Show resolved Hide resolved
local filter="${5:-}"

# Counter file tracks the number of benchmark executions so far.
counter_file="${min_file%.*}.counter"
counter=$(cat "${counter_file}" 2>/dev/null || echo "-1")
# Quickly execute the benchmarks initially to identify any broken ones.
[ "${counter}" -eq "-1" ] && quick="yes" || quick="no"
[ -f "${min_file}" ] || counter="-1"
# Execute benchmark if needed.
if [ "${counter}" -lt "${i}" ]; then
echo "==> Running ${name} benchmarks ($((counter + 1)) of ${REPEAT})" >&2
QUICK="${quick}" BENCH="${bench}" MIN_FILE="${min_file}" FILTER="${filter}" \
"${RUN_BENCHMARK}"
echo "$((counter + 1))" >"${counter_file}"
fi
# Summarize results if the benchmark was executed or if it's the final iteration.
if [ "${counter}" -lt "${i}" -o "${i}" = "${REPEAT}" ]; then
echo "==> Summarizing ${name} results:" >&2
NAME="${name}" MIN_FILE="${min_file}" "${SUMMARIZE_RESULTS}"
fi
}

for i in $(seq 0 "${REPEAT}"); do
run "${i}" "Embedders Compilation" \
"//rs/embedders:compilation_bench" "EMBEDDERS_COMPILATION.min"
run "${i}" "Embedders Heap" \
"//rs/embedders:heap_bench" "EMBEDDERS_HEAP.min"
run "${i}" "Embedders Stable Memory" \
"//rs/embedders:stable_memory_bench" "EMBEDDERS_STABLE_MEMORY.min"
run "${i}" "System API Inspect Message" \
"//rs/execution_environment:execute_inspect_message_bench" "SYSTEM_API_INSPECT_MESSAGE.min"
run "${i}" "System API Query" \
"//rs/execution_environment:execute_query_bench" "SYSTEM_API_QUERY.min"
run "${i}" "System API Update" \
"//rs/execution_environment:execute_update_bench" "SYSTEM_API_UPDATE.min"
run "${i}" "Wasm Instructions" \
"//rs/execution_environment:wasm_instructions_bench" "WASM_INSTRUCTIONS.min"
done
61 changes: 61 additions & 0 deletions rs/execution_environment/benches/run-benchmark.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#!/usr/bin/env bash
set -ue
##
## Helper script to run the specified `BENCH`
## and store the best (minimum) results in the `MIN_FILE`.
##

DEPENDENCIES="awk bash bazel rg sed tail tee"
which ${DEPENDENCIES} >/dev/null || (echo "Error checking dependencies: ${DEPENDENCIES}" >&2 && exit 1)

QUICK="${QUICK:-no}"
[ "${QUICK}" = "no" ] || BENCH_ARGS="--quick --output-format=bencher"

printf " %-12s := %s\n" \
"BENCH" "${BENCH:?Usage: BENCH='//rs/embedders:heap_bench' ${0}}" \
"BENCH_ARGS" "${BENCH_ARGS:=--warm-up-time=1 --measurement-time=1 --output-format=bencher}" \
"FILTER" "${FILTER:=}" \
"MIN_FILE" "${MIN_FILE:=${0##*/}.min}" \
"LOG_FILE" "${LOG_FILE:=${MIN_FILE%.*}.log}" >&2

TMP_FILE="${TMP_FILE:-${MIN_FILE%.*}.tmp}"

# Run the benchmark and capture its output in the `LOG_FILE`.
bash -c "set -o pipefail; \
bazel run '${BENCH}' -- ${FILTER} ${BENCH_ARGS} \
2>&1 | tee '${LOG_FILE}' | rg '^(test .* )?bench:' --line-buffered \
| sed -uEe 's/^test (.+) ... bench: +/> bench: \1 /' -Ee 's/^bench: +/> quick: /'" \
|| (
echo "Error running the benchmark:"
tail -10 "${LOG_FILE}" | sed 's/^/! /'
echo "For more details see: ${LOG_FILE}"
exit 1
) >&2

if ! [ -s "${MIN_FILE}" ]; then
echo " Storing results in ${MIN_FILE}" >&2
cat "${LOG_FILE}" | rg "^test .* bench:" >"${MIN_FILE}" \
|| echo " No results found in ${LOG_FILE} (quick run?)" >&2
else
echo " Merging ${LOG_FILE} into ${MIN_FILE}" >&2
rm -f "${TMP_FILE}"
cat "${LOG_FILE}" | rg "^test .* bench:" | while read new_bench; do
name="${new_bench#test }"
name="${name% ... bench:*}"
new_result_ns="${new_bench#* ... bench: }"
new_result_ns="${new_result_ns% ns/iter*}"

min_bench=$(rg -F "test ${name} ... bench:" "${MIN_FILE}" || true)
min_result_ns="${min_bench#* ... bench: }"
min_result_ns="${min_result_ns% ns/iter*}"

if [ -z "${min_result_ns}" ] || [ "${new_result_ns}" -lt "${min_result_ns}" ]; then
echo "^ improved: ${name} time: $((new_result_ns / 1000)) µs"
min_bench="${new_bench}"
fi
echo "${min_bench}" >>"${TMP_FILE}"
done
echo " Updating results in ${MIN_FILE}" >&2
mv -f "${TMP_FILE}" "${MIN_FILE}" 2>/dev/null || echo " No results to update (quick run?)" >&2
fi
rm -f "${LOG_FILE}"
74 changes: 74 additions & 0 deletions rs/execution_environment/benches/summarize-results.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#!/usr/bin/env bash
set -ue
##
## Helper script to summarize the results in the `MIN_FILE`
## comparing them to the `BASELINE_DIR` results.
##

DEPENDENCIES="awk rg sed"
which ${DEPENDENCIES} >/dev/null || (echo "Error checking dependencies: ${DEPENDENCIES}" >&2 && exit 1)

printf " %-12s := %s\n" \
"MIN_FILE" "${MIN_FILE:=${0##*/}.min}" \
"BASELINE_DIR" "${BASELINE_DIR:=${0%/*}/baseline}" >&2

NAME="${NAME:-${MIN_FILE%.*}}"
TMP_FILE="${TMP_FILE:-${MIN_FILE%.*}.tmp}"

if [ ! -s "${MIN_FILE}" ]; then
echo " No results to summarize in ${MIN_FILE} (quick run?)" >&2 && exit 0
fi
BASELINE_FILE="${BASELINE_DIR}/${MIN_FILE##*/}"
if [ ! -s "${BASELINE_FILE}" ]; then
echo " No baseline found in ${BASELINE_FILE}" >&2 && exit 0
fi

echo_diff() {
diff=$(((${2} - ${1}) * 100 * 10 / ${1}))
awk "BEGIN { print (${diff})^2 <= (2 * 10)^2 ? 0 : ${diff} / 10 }"
}

# Compare the `MIN_FILE` to `BASELINE_FILE`.
total_baseline_ns="0"
total_new_ns="0"
rm -f "${TMP_FILE}"
# Example content:
# test update/wasm64/baseline/empty loop ... bench: 2720243 ns/iter (+/- 48904)
while read min_bench; do
name="${min_bench#test }"
name="${name% ... bench:*}"
new_result_ns="${min_bench#* ... bench: }"
new_result_ns="${new_result_ns% ns/iter*}"

baseline_bench=$(rg -F "test ${name} ... bench:" "${BASELINE_FILE}" || true)
baseline_result_ns="${baseline_bench#* ... bench: }"
baseline_result_ns="${baseline_result_ns% ns/iter*}"

if [ -n "${new_result_ns}" -a -n "${baseline_result_ns}" ]; then
total_baseline_ns=$((total_baseline_ns + baseline_result_ns))
total_new_ns=$((total_new_ns + new_result_ns))
echo "$(echo_diff "${baseline_result_ns}" "${new_result_ns}") ${name}" >>"${TMP_FILE}"
fi
done <"${MIN_FILE}"

# Produce a summary.
baseline_commit=$(git rev-list --abbrev-commit -1 HEAD "${BASELINE_FILE}")
min_commit=$(git rev-list --abbrev-commit -1 HEAD)
total_diff=$(echo_diff "${total_baseline_ns}" "${total_new_ns}")
printf "= ${baseline_commit}..${min_commit}: ${NAME} total time: $((total_new_ns / 1000 / 1000)) ms "
case "${total_diff}" in
0) echo "(no change)" ;;
-*) echo "(improved by ${total_diff}%)" ;;
*) echo "(regressed by ${total_diff}%)" ;;
esac

# Produce top regressed/improved details.
if [ "${total_diff}" != "0" ]; then
cat "${TMP_FILE}" | sort -rn | rg '^[1-9]' | head -5 | while read diff name; do
echo "+ ${name} time regressed by ${diff}%"
done
cat "${TMP_FILE}" | sort -n | rg '^-' | head -5 | while read diff name; do
echo "- ${name} time improved by ${diff}%"
done
fi
# rm -f "${TMP_FILE}"
Loading