/ thirdparty / hyperfine / scripts / plot_histogram.py
plot_histogram.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  """This program shows `hyperfine` benchmark results as a histogram."""
 12  
 13  import argparse
 14  import json
 15  
 16  import matplotlib.pyplot as plt
 17  import numpy as np
 18  
 19  parser = argparse.ArgumentParser(description=__doc__)
 20  parser.add_argument("file", help="JSON file with benchmark results")
 21  parser.add_argument("--title", help="Plot title")
 22  parser.add_argument(
 23      "--labels", help="Comma-separated list of entries for the plot legend"
 24  )
 25  parser.add_argument("--bins", help="Number of bins (default: auto)")
 26  parser.add_argument(
 27      "--legend-location",
 28      help="Location of the legend on plot (default: upper center)",
 29      choices=[
 30          "upper center",
 31          "lower center",
 32          "right",
 33          "left",
 34          "best",
 35          "upper left",
 36          "upper right",
 37          "lower left",
 38          "lower right",
 39          "center left",
 40          "center right",
 41          "center",
 42      ],
 43      default="upper center",
 44  )
 45  parser.add_argument(
 46      "--type", help="Type of histogram (*bar*, barstacked, step, stepfilled)"
 47  )
 48  parser.add_argument("-o", "--output", help="Save image to the given filename.")
 49  parser.add_argument(
 50      "--t-min", metavar="T", help="Minimum time to be displayed (seconds)"
 51  )
 52  parser.add_argument(
 53      "--t-max", metavar="T", help="Maximum time to be displayed (seconds)"
 54  )
 55  parser.add_argument(
 56      "--log-count",
 57      help="Use a logarithmic y-axis for the event count",
 58      action="store_true",
 59  )
 60  
 61  args = parser.parse_args()
 62  
 63  with open(args.file) as f:
 64      results = json.load(f)["results"]
 65  
 66  if args.labels:
 67      labels = args.labels.split(",")
 68  else:
 69      labels = [b["command"] for b in results]
 70  all_times = [b["times"] for b in results]
 71  
 72  t_min = float(args.t_min) if args.t_min else np.min(list(map(np.min, all_times)))
 73  t_max = float(args.t_max) if args.t_max else np.max(list(map(np.max, all_times)))
 74  
 75  bins = int(args.bins) if args.bins else "auto"
 76  histtype = args.type if args.type else "bar"
 77  
 78  plt.figure(figsize=(10, 5))
 79  plt.hist(
 80      all_times,
 81      label=labels,
 82      bins=bins,
 83      histtype=histtype,
 84      range=(t_min, t_max),
 85  )
 86  plt.legend(
 87      loc=args.legend_location,
 88      fancybox=True,
 89      shadow=True,
 90      prop={"size": 10, "family": ["Source Code Pro", "Fira Mono", "Courier New"]},
 91  )
 92  
 93  plt.xlabel("Time [s]")
 94  if args.title:
 95      plt.title(args.title)
 96  
 97  if args.log_count:
 98      plt.yscale("log")
 99  else:
100      plt.ylim(0, None)
101  
102  if args.output:
103      plt.savefig(args.output, dpi=600)
104  else:
105      plt.show()