github.com/aclements/go-misc@v0.0.0-20240129233631-2f6ede80790c/benchscripts/benchstat2 (about)

     1  #!/usr/bin/python3
     2  
     3  import os
     4  import sys
     5  import tempfile
     6  import subprocess
     7  import argparse
     8  import re
     9  
    10  def expandHash(commits, h):
    11      x = None
    12      for c in commits:
    13          if c.startswith(h):
    14              if x != None:
    15                  raise ValueError("ambiguous commit hash " + h)
    16              x = c
    17      return x
    18  
    19  def main():
    20      parser = argparse.ArgumentParser(description="disentangle benchmark output")
    21      parser.add_argument("-C", metavar="gitdir", help="git repo for resolving commit hashes", default=os.path.expanduser("~/go.dev"))
    22      parser.add_argument("-o", metavar="base", help="write output to base-commit.log instead of invoking benchstat")
    23      parser.add_argument("-benchsave", action="store_true", help="invoke benchsave instead of benchstat")
    24      parser.add_argument("-geomean", action="store_true", help="pass -geomean to benchstat")
    25      parser.add_argument("-delta-test", help="pass -delta-test to benchstat")
    26      parser.add_argument("logs", nargs="+", help="input benchmark log files")
    27      parser.add_argument("commits", nargs="*", help="commits to show")
    28      args = parser.parse_args()
    29  
    30      benchstat = args.o == None
    31      if benchstat:
    32          tmpdir = tempfile.TemporaryDirectory()
    33          args.o = os.path.join(tmpdir.name, "out")
    34  
    35      # Separate logs and commits arguments
    36      for i, arg in enumerate(args.logs):
    37          if re.fullmatch("[0-9a-fA-F]{5,}", arg):
    38              args.commits = args.logs[i:]
    39              args.logs = args.logs[:i]
    40              break
    41          if arg == "--":
    42              args.commits = args.logs[i+1:]
    43              args.logs = args.logs[:i]
    44              break
    45  
    46      # Process input files into output files
    47      fmap = {}
    48      logCommits = set()
    49      for inp in args.logs:
    50          parseInput(inp, args.o, fmap, logCommits)
    51      for f, name in fmap.values():
    52          f.close()
    53  
    54      # Get commit order
    55      listArgs = [list(logCommits)]
    56      if args.commits:
    57          # We want to accept revision list arguments, but keep things
    58          # in argument order if there's more than one argument. This
    59          # means we have to call rev-list separately for each argument.
    60          listArgs = [["--no-walk", c] for c in args.commits]
    61      commits = []
    62      for listArg in listArgs:
    63          commits += subprocess.check_output(["git", "-C", args.C, "rev-list", "--topo-order", "--reverse"] + listArg, universal_newlines=True).splitlines()
    64      order = {cid: i for i, cid in enumerate(commits)}
    65  
    66      # Get names in commit order.
    67      if args.commits:
    68          names = [args.o + "-" + expandHash(commits, h)[:10] + ".log" for h in commits]
    69      else:
    70          names = [fmap[cid][1]
    71                   for cid in sorted(fmap.keys(), key=lambda cid: order[cid])]
    72  
    73      if benchstat:
    74          # Invoke benchstat/benchsave
    75          try:
    76              os.chdir(os.path.dirname(args.o))
    77              if args.benchsave:
    78                  benchargs = ["benchsave"]
    79              else:
    80                  benchargs = ["benchstat"]
    81                  if args.geomean:
    82                      benchargs.append("-geomean")
    83                  if args.delta_test:
    84                      benchargs.extend(["-delta-test", args.delta_test])
    85              subprocess.check_call(benchargs + list(map(os.path.basename, names)),
    86                                    stdout=sys.stdout, stderr=sys.stderr)
    87          finally:
    88              # Allow deletion of temporary directory.
    89              os.chdir("/")
    90      else:
    91          print(" ".join(names))
    92  
    93  def parseInput(path, outbase, fmap, logCommits):
    94      infile = open(path)
    95      outfile = None
    96  
    97      f = None
    98      for l in infile:
    99          if l.startswith("commit: "):
   100              chash = l.split()[1].strip()
   101              logCommits.add(chash)
   102              f, name = fmap.get(chash, (None, None))
   103              if f is None:
   104                  name = outbase + "-" + chash[:10] + ".log"
   105                  f = open(name, "w")
   106                  fmap[chash] = (f, name)
   107          elif f:
   108              f.write(l)
   109  
   110  main()