bm_main.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. #!/usr/bin/env python3
  2. #
  3. # Copyright 2017 gRPC authors.
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the "License");
  6. # you may not use this file except in compliance with the License.
  7. # You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS,
  13. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. """ Runs the entire bm_*.py pipeline, and possible comments on the PR """
  17. import argparse
  18. import multiprocessing
  19. import os
  20. import random
  21. import subprocess
  22. import sys
  23. sys.path.append(
  24. os.path.join(os.path.dirname(sys.argv[0]), '..', '..', 'run_tests',
  25. 'python_utils'))
  26. sys.path.append(
  27. os.path.join(os.path.dirname(sys.argv[0]), '..', '..', '..', 'run_tests',
  28. 'python_utils'))
  29. import bm_build
  30. import bm_constants
  31. import bm_diff
  32. import bm_run
  33. import check_on_pr
  34. import jobset
  35. def _args():
  36. argp = argparse.ArgumentParser(
  37. description='Perform diff on microbenchmarks')
  38. argp.add_argument('-t',
  39. '--track',
  40. choices=sorted(bm_constants._INTERESTING),
  41. nargs='+',
  42. default=sorted(bm_constants._INTERESTING),
  43. help='Which metrics to track')
  44. argp.add_argument('-b',
  45. '--benchmarks',
  46. nargs='+',
  47. choices=bm_constants._AVAILABLE_BENCHMARK_TESTS,
  48. default=bm_constants._AVAILABLE_BENCHMARK_TESTS,
  49. help='Which benchmarks to run')
  50. argp.add_argument('-d',
  51. '--diff_base',
  52. type=str,
  53. help='Commit or branch to compare the current one to')
  54. argp.add_argument(
  55. '-o',
  56. '--old',
  57. default='old',
  58. type=str,
  59. help='Name of baseline run to compare to. Usually just called "old"')
  60. argp.add_argument('-r',
  61. '--regex',
  62. type=str,
  63. default="",
  64. help='Regex to filter benchmarks run')
  65. argp.add_argument(
  66. '-l',
  67. '--loops',
  68. type=int,
  69. default=10,
  70. help=
  71. 'Number of times to loops the benchmarks. More loops cuts down on noise'
  72. )
  73. argp.add_argument('-j',
  74. '--jobs',
  75. type=int,
  76. default=multiprocessing.cpu_count(),
  77. help='Number of CPUs to use')
  78. argp.add_argument('--pr_comment_name',
  79. type=str,
  80. default="microbenchmarks",
  81. help='Name that Jenkins will use to comment on the PR')
  82. argp.add_argument('--counters', dest='counters', action='store_true')
  83. argp.add_argument('--no-counters', dest='counters', action='store_false')
  84. argp.set_defaults(counters=True)
  85. args = argp.parse_args()
  86. assert args.diff_base or args.old, "One of diff_base or old must be set!"
  87. if args.loops < 3:
  88. print("WARNING: This run will likely be noisy. Increase loops.")
  89. return args
  90. def eintr_be_gone(fn):
  91. """Run fn until it doesn't stop because of EINTR"""
  92. def inner(*args):
  93. while True:
  94. try:
  95. return fn(*args)
  96. except IOError as e:
  97. if e.errno != errno.EINTR:
  98. raise
  99. return inner
  100. def main(args):
  101. bm_build.build('new', args.benchmarks, args.jobs, args.counters)
  102. old = args.old
  103. if args.diff_base:
  104. old = 'old'
  105. where_am_i = subprocess.check_output(
  106. ['git', 'rev-parse', '--abbrev-ref', 'HEAD']).strip()
  107. subprocess.check_call(['git', 'checkout', args.diff_base])
  108. try:
  109. bm_build.build(old, args.benchmarks, args.jobs, args.counters)
  110. finally:
  111. subprocess.check_call(['git', 'checkout', where_am_i])
  112. subprocess.check_call(['git', 'submodule', 'update'])
  113. jobs_list = []
  114. jobs_list += bm_run.create_jobs('new', args.benchmarks, args.loops,
  115. args.regex, args.counters)
  116. jobs_list += bm_run.create_jobs(old, args.benchmarks, args.loops,
  117. args.regex, args.counters)
  118. # shuffle all jobs to eliminate noise from GCE CPU drift
  119. random.shuffle(jobs_list, random.SystemRandom().random)
  120. jobset.run(jobs_list, maxjobs=args.jobs)
  121. diff, note, significance = bm_diff.diff(args.benchmarks, args.loops,
  122. args.regex, args.track, old, 'new',
  123. args.counters)
  124. if diff:
  125. text = '[%s] Performance differences noted:\n%s' % (
  126. args.pr_comment_name, diff)
  127. else:
  128. text = '[%s] No significant performance differences' % args.pr_comment_name
  129. if note:
  130. text = note + '\n\n' + text
  131. print('%s' % text)
  132. check_on_pr.check_on_pr('Benchmark', '```\n%s\n```' % text)
  133. check_on_pr.label_significance_on_pr('perf-change', significance)
  134. if __name__ == '__main__':
  135. args = _args()
  136. main(args)