Skip to content

Commit d156bd9

Browse files
feat(bench): add bench output scripts (#123)
* feat(bench): add bench output scripts * fix(makefile): spelling error * fix(scripts): remove extra whitespace for consistency
1 parent 2aa0417 commit d156bd9

File tree

3 files changed

+126
-1
lines changed

3 files changed

+126
-1
lines changed

Makefile

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ bench: build
3636
run-benchmarks: bench
3737
@cmake --build "$(BUILD_DIR)" --target run_all_benchmarks -- -j$(JOBS)
3838

39+
bench-diff:
40+
@python tools/scripts/compare_benchmarks.py
41+
42+
bench-out:
43+
@python tools/scripts/output_benchmarks.py
44+
3945
tools: BUILD_TOOLS := ON
4046
tools: build
4147

@@ -54,9 +60,11 @@ help:
5460
@echo " test - run ctest (RUN_TESTS=ON)"
5561
@echo " bench - build & run benchmarks (BUILD_BENCH=ON)"
5662
@echo " run-benchmarks - run all benchmarks"
63+
@echo " bench-diff - compare the two most recent benchmark runs"
64+
@echo " bench-out - output the most recent benchmark run"
5765
@echo " tools - build project-defined tools (BUILD_TOOLS=ON)"
5866
@echo " format - run clang-format on all source files"
59-
@echo " clean - clean build artefacts"
67+
@echo " clean - clean build artifacts"
6068
@echo " distclean - clean entire build dir"
6169
@echo ""
6270
@echo "Knobs (override with VAR=value):"
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Copyright (c) Brandon Pacewic
2+
# SPDX-License-Identifier: MIT
3+
4+
import os
5+
import re
6+
import subprocess
7+
from pathlib import Path
8+
from collections import defaultdict
9+
10+
BUILD_DIR = "build"
11+
RESULTS_DIR = Path(BUILD_DIR) / "benchmarks" / "results"
12+
GOOGLE_BENCHMARK_COMPARE_SCRIPT = Path("../../../benchmarks/benchmark/tools/compare.py")
13+
14+
15+
def extract_algo_name(filename: str) -> str:
16+
# Removes timestamp and UUID prefix
17+
# Matches: 2025-04-17_13-23-38_abcdef_algo.json
18+
return re.sub(r"^[0-9]{4}-[0-9]{2}-[0-9]{2}_[^_]+_[^_]+_", "", filename)
19+
20+
21+
def main() -> None:
22+
print("Comparing most recent benchmarks per algorithm...")
23+
if not RESULTS_DIR.is_dir():
24+
print(f"Results directory '{RESULTS_DIR}' not found.")
25+
return
26+
27+
algo_files = defaultdict(list)
28+
for file in sorted(RESULTS_DIR.glob("*.json"), key=os.path.getmtime, reverse=True):
29+
algo = extract_algo_name(file.name)
30+
algo_files[algo].append(file)
31+
32+
for algo, files in algo_files.items():
33+
if len(files) < 2:
34+
print(f"Skipping {algo} (need at least 2 versions)")
35+
continue
36+
37+
new, old = files[:2]
38+
name = algo.replace(".json", "").replace(".cpp", "")
39+
40+
print("────────────────────────────────────────────────────────────")
41+
print(f"Algorithm: {name}")
42+
print("Comparing most recent two runs:")
43+
print(f" OLD: {old.name}")
44+
print(f" NEW: {new.name}\n")
45+
46+
try:
47+
subprocess.run(
48+
["python", str(GOOGLE_BENCHMARK_COMPARE_SCRIPT), "benchmarks", old.name, new.name],
49+
cwd=RESULTS_DIR,
50+
check=False,
51+
)
52+
except Exception as e:
53+
print(f"Error running compare script: {e}")
54+
print()
55+
56+
57+
if __name__ == "__main__":
58+
main()

tools/scripts/output_benchmarks.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Copyright (c) Brandon Pacewic
2+
# SPDX-License-Identifier: MIT
3+
4+
import os
5+
import re
6+
import json
7+
from pathlib import Path
8+
from collections import defaultdict
9+
10+
BUILD_DIR = "build"
11+
RESULTS_DIR = Path(BUILD_DIR) / "benchmarks" / "results"
12+
13+
14+
def extract_algo_name(filename: str) -> str:
15+
# Removes timestamp and UUID prefix
16+
# Matches: 2025-04-17_13-23-38_abcdef_algo.json
17+
return re.sub(r"^[0-9]{4}-[0-9]{2}-[0-9]{2}_[^_]+_[^_]+_", "", filename)
18+
19+
20+
def print_benchmark_data(file_path: Path) -> None:
21+
try:
22+
with open(file_path, 'r') as f:
23+
data = json.load(f)
24+
25+
for bench in data.get("benchmarks", []):
26+
name = bench.get("name", "")
27+
real_time = bench.get("real_time", 0.0)
28+
cpu_time = bench.get("cpu_time", 0.0)
29+
iterations = bench.get("iterations", 0)
30+
print(f"{name:<30} Time: {real_time:>10.2f} CPU: {cpu_time:>10.2f} Iter: {iterations}")
31+
except Exception as e:
32+
print(f"Error reading {file_path.name}: {e}")
33+
34+
35+
def main() -> None:
36+
print("Showing most recent benchmark outputs per algorithm...")
37+
if not RESULTS_DIR.is_dir():
38+
print(f"Results directory '{RESULTS_DIR}' not found.")
39+
return
40+
41+
algo_to_files = defaultdict(list)
42+
for file in sorted(RESULTS_DIR.glob("*.json"), key=os.path.getmtime, reverse=True):
43+
algo_name = extract_algo_name(file.name)
44+
algo_to_files[algo_name].append(file)
45+
46+
for algo, files in algo_to_files.items():
47+
latest = files[0]
48+
display_name = algo.replace(".json", "").replace(".cpp", "")
49+
50+
print("────────────────────────────────────────────────────────────")
51+
print(f"Algorithm: {display_name}")
52+
print(f"File: {latest.name}\n")
53+
54+
print_benchmark_data(latest)
55+
print()
56+
57+
58+
if __name__ == "__main__":
59+
main()

0 commit comments

Comments
 (0)