run_benchmarks.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. #!/usr/bin/env python3
  2. # Copyright Hans Dembinski 2019
  3. # Distributed under the Boost Software License, Version 1.0.
  4. # See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt
  5. """
  6. This script runs the benchmarks on previous versions of this library to track changes
  7. in performance.
  8. Run this from a special build directory that uses the benchmark folder as root
  9. cd my_build_dir
  10. cmake ../benchmark
  11. ../run_benchmarks.py
  12. This creates a database, benchmark_results. Plot it:
  13. ../plot_benchmarks.py
  14. The script leaves the include folder in a modified state. To clean up, do:
  15. git checkout HEAD -- ../include
  16. git clean -f -- ../include
  17. """
  18. import subprocess as subp
  19. import tempfile
  20. import os
  21. import shelve
  22. import json
  23. import argparse
  24. def get_commits():
  25. commits = []
  26. comments = {}
  27. for line in subp.check_output(("git", "log", "--oneline")).decode("ascii").split("\n"):
  28. if line:
  29. ispace = line.index(" ")
  30. hash = line[:ispace]
  31. commits.append(hash)
  32. comments[hash] = line[ispace+1:]
  33. commits = commits[::-1]
  34. return commits, comments
  35. def recursion(results, commits, comments, ia, ib):
  36. ic = int((ia + ib) / 2)
  37. if ic == ia:
  38. return
  39. run(results, comments, commits[ic], False)
  40. if all([results[commits[i]] is None for i in (ia, ib, ic)]):
  41. return
  42. recursion(results, commits, comments, ic, ib)
  43. recursion(results, commits, comments, ia, ic)
  44. def run(results, comments, hash, update):
  45. if not update and hash in results:
  46. return
  47. print(hash, comments[hash])
  48. subp.call(("rm", "-rf", "../include"))
  49. if subp.call(("git", "checkout", hash, "--", "../include")) != 0:
  50. print("[Benchmark] Cannot checkout include folder\n")
  51. return
  52. print(hash, "make")
  53. with tempfile.TemporaryFile() as out:
  54. if subp.call(("make", "-j4", "histogram_filling"), stdout=out, stderr=out) != 0:
  55. print("[Benchmark] Cannot make benchmarks\n")
  56. out.seek(0)
  57. print(out.read().decode("utf-8") + "\n")
  58. return
  59. print(hash, "run")
  60. s = subp.check_output(("./histogram_filling", "--benchmark_format=json", "--benchmark_filter=normal"))
  61. d = json.loads(s)
  62. if update and hash in results and results[hash] is not None:
  63. d2 = results[hash]
  64. for i, (b, b2) in enumerate(zip(d["benchmarks"], d2["benchmarks"])):
  65. d["benchmarks"][i] = b if b["cpu_time"] < b2["cpu_time"] else b2
  66. results[hash] = d
  67. for benchmark in d["benchmarks"]:
  68. print(benchmark["name"], min(benchmark["real_time"], benchmark["cpu_time"]))
  69. def main():
  70. commits, comments = get_commits()
  71. parser = argparse.ArgumentParser(description=__doc__,
  72. formatter_class=argparse.RawDescriptionHelpFormatter)
  73. parser.add_argument("first", type=str, default="begin",
  74. help="first commit in range, special value `begin` is allowed")
  75. parser.add_argument("last", type=str, default="end",
  76. help="last commit in range, special value `end` is allowed")
  77. parser.add_argument("-f", action="store_true",
  78. help="override previous results")
  79. args = parser.parse_args()
  80. if args.first == "begin":
  81. args.first = commits[0]
  82. if args.last == "end":
  83. args.last = commits[-1]
  84. with shelve.open("benchmark_results") as results:
  85. a = commits.index(args.first)
  86. b = commits.index(args.last)
  87. if args.f:
  88. for hash in commits[a:b+1]:
  89. del results[hash]
  90. run(results, comments, args.first, False)
  91. run(results, comments, args.last, False)
  92. recursion(results, commits, comments, a, b)
  93. if __name__ == "__main__":
  94. main()