Skip to content

Commit 292c135

Browse files
authored
[libc++] Add new utilities to compare benchmark results between builds (#120743)
Also, add documentation for it.
1 parent 15d3e4a commit 292c135

File tree

3 files changed

+142
-0
lines changed

3 files changed

+142
-0
lines changed

libcxx/docs/TestingLibcxx.rst

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,29 @@ we only want to make sure they don't rot. Do not rely on the results of benchmar
459459
run through ``check-cxx`` for anything, instead run the benchmarks manually using
460460
the instructions for running individual tests.
461461

462+
If you want to compare the results of different benchmark runs, we recommend using the
463+
``libcxx-compare-benchmarks`` helper tool. First, configure CMake in a build directory
464+
and run the benchmark:
465+
466+
.. code-block:: bash
467+
468+
$ cmake -S runtimes -B <build1> [...]
469+
$ libcxx/utils/libcxx-lit <build1> libcxx/test/benchmarks/string.bench.cpp --param optimization=speed
470+
471+
Then, do the same for the second configuration you want to test. Use a different build
472+
directory for that configuration:
473+
474+
.. code-block:: bash
475+
476+
$ cmake -S runtimes -B <build2> [...]
477+
$ libcxx/utils/libcxx-lit <build2> libcxx/test/benchmarks/string.bench.cpp --param optimization=speed
478+
479+
Finally, use ``libcxx-compare-benchmarks`` to compare both:
480+
481+
.. code-block:: bash
482+
483+
$ libcxx/utils/libcxx-compare-benchmarks <build1> <build2> libcxx/test/benchmarks/string.bench.cpp
484+
462485
.. _`Google Benchmark`: https://github.com/google/benchmark
463486

464487
.. _testing-hardening-assertions:

libcxx/utils/libcxx-benchmark-json

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#!/usr/bin/env bash
2+
3+
set -e
4+
5+
PROGNAME="$(basename "${0}")"
6+
MONOREPO_ROOT="$(realpath $(dirname "${PROGNAME}"))"
7+
function usage() {
8+
cat <<EOF
9+
Usage:
10+
${PROGNAME} [-h|--help] <build-directory> benchmarks...
11+
12+
Print the path to the JSON files containing benchmark results for the given benchmarks.
13+
14+
This requires those benchmarks to have already been run, i.e. this only resolves the path
15+
to the benchmark .json file within the build directory.
16+
17+
<build-directory> The path to the build directory.
18+
benchmarks... Paths of the benchmarks to extract the results for. Those paths are relative to '<monorepo-root>'.
19+
20+
Example
21+
=======
22+
$ cmake -S runtimes -B build/ -DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi"
23+
$ libcxx-lit build/ -sv libcxx/test/benchmarks/algorithms/for_each.bench.cpp
24+
$ less \$(${PROGNAME} build/ libcxx/test/benchmarks/algorithms/for_each.bench.cpp)
25+
EOF
26+
}
27+
28+
if [[ "${1}" == "-h" || "${1}" == "--help" ]]; then
29+
usage
30+
exit 0
31+
fi
32+
33+
if [[ $# -lt 1 ]]; then
34+
usage
35+
exit 1
36+
fi
37+
38+
build_dir="${1}"
39+
shift
40+
41+
for benchmark in ${@}; do
42+
# Normalize the paths by turning all benchmarks paths into absolute ones and then making them
43+
# relative to the root of the monorepo.
44+
benchmark="$(realpath ${benchmark})"
45+
relative=$(python -c "import os; import sys; print(os.path.relpath(sys.argv[1], sys.argv[2]))" "${benchmark}" "${MONOREPO_ROOT}")
46+
47+
# Extract components of the benchmark path
48+
directory="$(dirname ${relative})"
49+
file="$(basename ${relative})"
50+
51+
# Reconstruct the (slightly weird) path to the benchmark json file. This should be kept in sync
52+
# whenever the test suite changes.
53+
json="${build_dir}/${directory}/Output/${file}.dir/benchmark-result.json"
54+
if [[ -f "${json}" ]]; then
55+
echo "${json}"
56+
fi
57+
done
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#!/usr/bin/env bash
2+
3+
set -e
4+
5+
PROGNAME="$(basename "${0}")"
6+
MONOREPO_ROOT="$(realpath $(dirname "${PROGNAME}"))"
7+
function usage() {
8+
cat <<EOF
9+
Usage:
10+
${PROGNAME} [-h|--help] <baseline-build> <candidate-build> benchmarks...
11+
12+
Compare the given benchmarks between the baseline and the candidate build directories.
13+
14+
This requires those benchmarks to have already been generated in both build directories.
15+
16+
<baseline-build> The path to the build directory considered the baseline.
17+
<candidate-build> The path to the build directory considered the candidate.
18+
benchmarks... Paths of the benchmarks to compare. Those paths are relative to '<monorepo-root>'.
19+
20+
Example
21+
=======
22+
$ libcxx-lit build1/ -sv libcxx/test/benchmarks/algorithms/for_each.bench.cpp
23+
$ libcxx-lit build2/ -sv libcxx/test/benchmarks/algorithms/for_each.bench.cpp
24+
$ ${PROGNAME} build1/ build2/ libcxx/test/benchmarks/algorithms/for_each.bench.cpp
25+
EOF
26+
}
27+
28+
if [[ "${1}" == "-h" || "${1}" == "--help" ]]; then
29+
usage
30+
exit 0
31+
fi
32+
33+
if [[ $# -lt 1 ]]; then
34+
usage
35+
exit 1
36+
fi
37+
38+
baseline="${1}"
39+
candidate="${2}"
40+
shift; shift
41+
42+
GBENCH="${MONOREPO_ROOT}/third-party/benchmark"
43+
44+
python3 -m venv /tmp/libcxx-compare-benchmarks-venv
45+
source /tmp/libcxx-compare-benchmarks-venv/bin/activate
46+
pip3 install -r ${GBENCH}/tools/requirements.txt
47+
48+
for benchmark in ${@}; do
49+
base="$(${MONOREPO_ROOT}/libcxx/utils/libcxx-benchmark-json ${baseline} ${benchmark})"
50+
cand="$(${MONOREPO_ROOT}/libcxx/utils/libcxx-benchmark-json ${candidate} ${benchmark})"
51+
52+
if [[ ! -e "${base}" ]]; then
53+
echo "Benchmark ${benchmark} does not exist in the baseline"
54+
continue
55+
fi
56+
if [[ ! -e "${cand}" ]]; then
57+
echo "Benchmark ${benchmark} does not exist in the candidate"
58+
continue
59+
fi
60+
61+
"${GBENCH}/tools/compare.py" benchmarks "${base}" "${cand}"
62+
done

0 commit comments

Comments
 (0)