qps_diff.py
Go to the documentation of this file.
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 """ Computes the diff between two qps runs and outputs significant results """
17 
18 import argparse
19 import json
20 import multiprocessing
21 import os
22 import shutil
23 import subprocess
24 import sys
25 
26 import qps_scenarios
27 import tabulate
28 
29 sys.path.append(
30  os.path.join(os.path.dirname(sys.argv[0]), '..', 'microbenchmarks',
31  'bm_diff'))
32 import bm_speedup
33 
34 sys.path.append(
35  os.path.join(os.path.dirname(sys.argv[0]), '..', '..', 'run_tests',
36  'python_utils'))
37 import check_on_pr
38 
39 
40 def _args():
41  argp = argparse.ArgumentParser(description='Perform diff on QPS Driver')
42  argp.add_argument('-d',
43  '--diff_base',
44  type=str,
45  help='Commit or branch to compare the current one to')
46  argp.add_argument(
47  '-l',
48  '--loops',
49  type=int,
50  default=4,
51  help='Number of loops for each benchmark. More loops cuts down on noise'
52  )
53  argp.add_argument('-j',
54  '--jobs',
55  type=int,
56  default=multiprocessing.cpu_count(),
57  help='Number of CPUs to use')
58  args = argp.parse_args()
59  assert args.diff_base, "diff_base must be set"
60  return args
61 
62 
63 def _make_cmd(jobs):
64  return ['make', '-j', '%d' % jobs, 'qps_json_driver', 'qps_worker']
65 
66 
67 def build(name, jobs):
68  shutil.rmtree('qps_diff_%s' % name, ignore_errors=True)
69  subprocess.check_call(['git', 'submodule', 'update'])
70  try:
71  subprocess.check_call(_make_cmd(jobs))
72  except subprocess.CalledProcessError as e:
73  subprocess.check_call(['make', 'clean'])
74  subprocess.check_call(_make_cmd(jobs))
75  os.rename('bins', 'qps_diff_%s' % name)
76 
77 
78 def _run_cmd(name, scenario, fname):
79  return [
80  'qps_diff_%s/opt/qps_json_driver' % name, '--scenarios_json', scenario,
81  '--json_file_out', fname
82  ]
83 
84 
85 def run(name, scenarios, loops):
86  for sn in scenarios:
87  for i in range(0, loops):
88  fname = "%s.%s.%d.json" % (sn, name, i)
89  subprocess.check_call(_run_cmd(name, scenarios[sn], fname))
90 
91 
92 def _load_qps(fname):
93  try:
94  with open(fname) as f:
95  return json.loads(f.read())['qps']
96  except IOError as e:
97  print(("IOError occurred reading file: %s" % fname))
98  return None
99  except ValueError as e:
100  print(("ValueError occurred reading file: %s" % fname))
101  return None
102 
103 
104 def _median(ary):
105  assert (len(ary))
106  ary = sorted(ary)
107  n = len(ary)
108  if n % 2 == 0:
109  return (ary[(n - 1) / 2] + ary[(n - 1) / 2 + 1]) / 2.0
110  else:
111  return ary[n / 2]
112 
113 
114 def diff(scenarios, loops, old, new):
115  old_data = {}
116  new_data = {}
117 
118  # collect data
119  for sn in scenarios:
120  old_data[sn] = []
121  new_data[sn] = []
122  for i in range(loops):
123  old_data[sn].append(_load_qps("%s.%s.%d.json" % (sn, old, i)))
124  new_data[sn].append(_load_qps("%s.%s.%d.json" % (sn, new, i)))
125 
126  # crunch data
127  headers = ['Benchmark', 'qps']
128  rows = []
129  for sn in scenarios:
130  mdn_diff = abs(_median(new_data[sn]) - _median(old_data[sn]))
131  print(('%s: %s=%r %s=%r mdn_diff=%r' %
132  (sn, new, new_data[sn], old, old_data[sn], mdn_diff)))
133  s = bm_speedup.speedup(new_data[sn], old_data[sn], 10e-5)
134  if abs(s) > 3 and mdn_diff > 0.5:
135  rows.append([sn, '%+d%%' % s])
136 
137  if rows:
138  return tabulate.tabulate(rows, headers=headers, floatfmt='+.2f')
139  else:
140  return None
141 
142 
143 def main(args):
144  build('new', args.jobs)
145 
146  if args.diff_base:
147  where_am_i = subprocess.check_output(
148  ['git', 'rev-parse', '--abbrev-ref', 'HEAD']).decode().strip()
149  subprocess.check_call(['git', 'checkout', args.diff_base])
150  try:
151  build('old', args.jobs)
152  finally:
153  subprocess.check_call(['git', 'checkout', where_am_i])
154  subprocess.check_call(['git', 'submodule', 'update'])
155 
156  run('new', qps_scenarios._SCENARIOS, args.loops)
157  run('old', qps_scenarios._SCENARIOS, args.loops)
158 
159  diff_output = diff(qps_scenarios._SCENARIOS, args.loops, 'old', 'new')
160 
161  if diff_output:
162  text = '[qps] Performance differences noted:\n%s' % diff_output
163  else:
164  text = '[qps] No significant performance differences'
165  print(('%s' % text))
166  check_on_pr.check_on_pr('QPS', '```\n%s\n```' % text)
167 
168 
169 if __name__ == '__main__':
170  args = _args()
171  main(args)
qps_diff._median
def _median(ary)
Definition: qps_diff.py:104
qps_diff.main
def main(args)
Definition: qps_diff.py:143
capstone.range
range
Definition: third_party/bloaty/third_party/capstone/bindings/python/capstone/__init__.py:6
build
Definition: build.py:1
qps_diff.run
def run(name, scenarios, loops)
Definition: qps_diff.py:85
qps_diff._make_cmd
def _make_cmd(jobs)
Definition: qps_diff.py:63
bm_speedup.speedup
def speedup(new, old, threshold=_DEFAULT_THRESHOLD)
Definition: bm_speedup.py:32
qps_diff._run_cmd
def _run_cmd(name, scenario, fname)
Definition: qps_diff.py:78
qps_diff._args
def _args()
Definition: qps_diff.py:40
qps_diff.build
def build(name, jobs)
Definition: qps_diff.py:67
main
Definition: main.py:1
grpc._common.decode
def decode(b)
Definition: grpc/_common.py:75
open
#define open
Definition: test-fs.c:46
len
int len
Definition: abseil-cpp/absl/base/internal/low_level_alloc_test.cc:46
qps_diff.diff
def diff(scenarios, loops, old, new)
Definition: qps_diff.py:114
qps_diff._load_qps
def _load_qps(fname)
Definition: qps_diff.py:92


grpc
Author(s):
autogenerated on Thu Mar 13 2025 03:00:59