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