/ thirdparty / hyperfine / scripts / plot_whisker.py
plot_whisker.py
 1  #!/usr/bin/env python
 2  # /// script
 3  # requires-python = ">=3.10"
 4  # dependencies = [
 5  #     "matplotlib",
 6  #     "pyqt6",
 7  # ]
 8  # ///
 9  
10  """This program shows `hyperfine` benchmark results as a box and whisker plot.
11  
12  Quoting from the matplotlib documentation:
13      The box extends from the lower to upper quartile values of the data, with
14      a line at the median. The whiskers extend from the box to show the range
15      of the data. Flier points are those past the end of the whiskers.
16  """
17  
18  import argparse
19  import json
20  
21  import matplotlib.pyplot as plt
22  
23  parser = argparse.ArgumentParser(description=__doc__)
24  parser.add_argument("file", help="JSON file with benchmark results")
25  parser.add_argument("--title", help="Plot Title")
26  parser.add_argument("--sort-by", choices=["median"], help="Sort method")
27  parser.add_argument(
28      "--labels", help="Comma-separated list of entries for the plot legend"
29  )
30  parser.add_argument("-o", "--output", help="Save image to the given filename.")
31  
32  args = parser.parse_args()
33  
34  with open(args.file, encoding="utf-8") as f:
35      results = json.load(f)["results"]
36  
37  if args.labels:
38      labels = args.labels.split(",")
39  else:
40      labels = [b["command"] for b in results]
41  times = [b["times"] for b in results]
42  
43  if args.sort_by == "median":
44      medians = [b["median"] for b in results]
45      indices = sorted(range(len(labels)), key=lambda k: medians[k])
46      labels = [labels[i] for i in indices]
47      times = [times[i] for i in indices]
48  
49  plt.figure(figsize=(10, 6), constrained_layout=True)
50  boxplot = plt.boxplot(times, vert=True, patch_artist=True)
51  cmap = plt.get_cmap("rainbow")
52  colors = [cmap(val / len(times)) for val in range(len(times))]
53  
54  for patch, color in zip(boxplot["boxes"], colors):
55      patch.set_facecolor(color)
56  
57  if args.title:
58      plt.title(args.title)
59  plt.legend(handles=boxplot["boxes"], labels=labels, loc="best", fontsize="medium")
60  plt.ylabel("Time [s]")
61  plt.ylim(0, None)
62  plt.xticks(list(range(1, len(labels) + 1)), labels, rotation=45)
63  if args.output:
64      plt.savefig(args.output)
65  else:
66      plt.show()