Skip to content

Commit ee4e4d0

Browse files
marxinsharkdp
authored andcommitted
Add plot_benchmarks scripts
The script is intended for a collection of hyperfine JSON files where multiple benchmarks were run for a selected group of commands.
1 parent 92cc4f2 commit ee4e4d0

File tree

1 file changed

+80
-0
lines changed

1 file changed

+80
-0
lines changed

scripts/plot_benchmarks.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#!/usr/bin/env python
2+
# /// script
3+
# requires-python = ">=3.10"
4+
# dependencies = [
5+
# "matplotlib",
6+
# "pyqt6",
7+
# "numpy",
8+
# ]
9+
# ///
10+
11+
# This program shows `hyperfine` benchmark results as a bar plot grouped by command.
12+
13+
import argparse
14+
import json
15+
import pathlib
16+
17+
import matplotlib.pyplot as plt
18+
import numpy as np
19+
20+
parser = argparse.ArgumentParser(description=__doc__)
21+
parser.add_argument(
22+
"files", nargs="+", type=pathlib.Path, help="JSON files with benchmark results"
23+
)
24+
parser.add_argument("--title", help="Plot Title")
25+
parser.add_argument(
26+
"--benchmark-names", nargs="+", help="Names of the benchmark groups"
27+
)
28+
parser.add_argument("-o", "--output", help="Save image to the given filename")
29+
30+
args = parser.parse_args()
31+
32+
commands = None
33+
data = []
34+
inputs = []
35+
36+
if args.benchmark_names:
37+
assert len(args.files) == len(
38+
args.benchmark_names
39+
), "Number of benchmark names must match the number of input files."
40+
41+
for i, filename in enumerate(args.files):
42+
with open(filename) as f:
43+
results = json.load(f)["results"]
44+
benchmark_commands = [b["command"] for b in results]
45+
if commands is None:
46+
commands = benchmark_commands
47+
else:
48+
assert (
49+
commands == benchmark_commands
50+
), f"Unexpected commands in {filename}: {benchmark_commands}, expected: {commands}"
51+
data.append([round(b["mean"], 2) for b in results])
52+
if args.benchmark_names:
53+
inputs.append(args.benchmark_names[i])
54+
else:
55+
inputs.append(filename.stem)
56+
57+
data = np.transpose(data)
58+
x = np.arange(len(inputs)) # the label locations
59+
width = 0.25 # the width of the bars
60+
61+
fig, ax = plt.subplots(layout="constrained")
62+
fig.set_figheight(5)
63+
fig.set_figwidth(10)
64+
for i, command in enumerate(commands):
65+
offset = width * (i + 1)
66+
rects = ax.bar(x + offset, data[i], width, label=command)
67+
68+
ax.set_xticks(x + 0.5, inputs)
69+
ax.grid(visible=True, axis="y")
70+
71+
if args.title:
72+
plt.title(args.title)
73+
plt.xlabel("Benchmark")
74+
plt.ylabel("Time [s]")
75+
plt.legend(title="Command")
76+
77+
if args.output:
78+
plt.savefig(args.output)
79+
else:
80+
plt.show()

0 commit comments

Comments
 (0)