Skip to content

Commit dc00a0f

Browse files
committed
Inital commit of perfcmp script
This compares the summary values for 2 perf runs. Right now you have to manually download the results files, but eventually I'd like to add a mode that can download from an S3 bucket.
1 parent 64b8155 commit dc00a0f

1 file changed

Lines changed: 153 additions & 0 deletions

File tree

scripts/performance/perfcmp

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
#!/usr/bin/env python
2+
"""Compare 2 perf runs.
3+
4+
To use, specify the local directories that contain
5+
the run information::
6+
7+
$ ./perfcmp /results/2016-01-01-1111/ /results/2016-01-01-2222/
8+
9+
"""
10+
import os
11+
import json
12+
import argparse
13+
14+
from colorama import Fore, Style
15+
from tabulate import tabulate
16+
17+
18+
class RunComparison(object):
19+
20+
MEMORY_FIELDS = ['average_memory', 'max_memory']
21+
TIME_FIELDS = ['total_time']
22+
# Fields that aren't memory or time fields, they require
23+
# no special formatting.
24+
OTHER_FIELDS = ['average_cpu']
25+
26+
def __init__(self, old_summary, new_summary):
27+
self.old_summary = old_summary
28+
self.new_summary = new_summary
29+
30+
def iter_field_names(self):
31+
for field in self.TIME_FIELDS + self.MEMORY_FIELDS + self.OTHER_FIELDS:
32+
yield field
33+
34+
def old(self, field):
35+
value = self.old_summary[field]
36+
return self._format(field, value)
37+
38+
def old_suffix(self, field):
39+
value = self.old_summary[field]
40+
return self._format_suffix(field, value)
41+
42+
def new_suffix(self, field):
43+
value = self.new_summary[field]
44+
return self._format_suffix(field, value)
45+
46+
def _format_suffix(self, field, value):
47+
if field in self.TIME_FIELDS:
48+
return 'sec'
49+
elif field in self.OTHER_FIELDS:
50+
return ''
51+
else:
52+
# The suffix depends on the actual value.
53+
return self._human_readable_size(value)[1]
54+
55+
def old_stddev(self, field):
56+
real_field = 'std_dev_%s' % field
57+
return self.old(real_field)
58+
59+
def new(self, field):
60+
value = self.new_summary[field]
61+
return self._format(field, value)
62+
63+
def new_stddev(self, field):
64+
real_field = 'std_dev_%s' % field
65+
return self.new(real_field)
66+
67+
def _format(self, field, value):
68+
if field.startswith('std_dev_'):
69+
field = field[len('std_dev_'):]
70+
if field in self.MEMORY_FIELDS:
71+
return self._human_readable_size(value)[0]
72+
elif field in self.TIME_FIELDS:
73+
return '%-3.2f' % value
74+
else:
75+
return '%.2f' % value
76+
77+
def _human_readable_size(self, value):
78+
hummanize_suffixes = ('KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB')
79+
base = 1024
80+
bytes_int = float(value)
81+
82+
if bytes_int == 1:
83+
return '1 Byte'
84+
elif bytes_int < base:
85+
return '%d Bytes' % bytes_int
86+
87+
for i, suffix in enumerate(hummanize_suffixes):
88+
unit = base ** (i+2)
89+
if round((bytes_int / unit) * base) < base:
90+
return ['%.2f' % (base * bytes_int / unit), suffix]
91+
92+
def diff_percent(self, field):
93+
diff_percent = (
94+
(self.new_summary[field] - self.old_summary[field]) /
95+
float(self.old_summary[field])) * 100
96+
return diff_percent
97+
98+
99+
def compare_runs(old_dir, new_dir):
100+
for dirname in os.listdir(old_dir):
101+
old_run_dir = os.path.join(old_dir, dirname)
102+
new_run_dir = os.path.join(new_dir, dirname)
103+
if not os.path.isdir(old_run_dir):
104+
continue
105+
old_summary = get_summary(old_run_dir)
106+
new_summary = get_summary(new_run_dir)
107+
comp = RunComparison(old_summary, new_summary)
108+
header = [Style.BRIGHT + dirname + Style.RESET_ALL,
109+
Style.BRIGHT + 'old' + Style.RESET_ALL,
110+
# Numeric suffix (MiB, GiB, sec).
111+
'',
112+
'std_dev',
113+
Style.BRIGHT + 'new' + Style.RESET_ALL,
114+
# Numeric suffix (MiB, GiB, sec).
115+
'',
116+
'std_dev',
117+
Style.BRIGHT + 'delta' + Style.RESET_ALL]
118+
rows = []
119+
for field in comp.iter_field_names():
120+
row = [field, comp.old(field), comp.old_suffix(field),
121+
comp.old_stddev(field), comp.new(field),
122+
comp.new_suffix(field), comp.new_stddev(field)]
123+
diff_percent = comp.diff_percent(field)
124+
diff_percent_str = '%.2f%%' % diff_percent
125+
if diff_percent < 0:
126+
diff_percent_str = (
127+
Fore.GREEN + diff_percent_str + Style.RESET_ALL)
128+
else:
129+
diff_percent_str = (
130+
Fore.RED + diff_percent_str + Style.RESET_ALL)
131+
row.append(diff_percent_str)
132+
rows.append(row)
133+
print(tabulate(rows, headers=header, tablefmt='plain'))
134+
print('')
135+
136+
137+
def get_summary(benchmark_dir):
138+
summary_json = os.path.join(benchmark_dir, 'summary.json')
139+
with open(summary_json) as f:
140+
return json.load(f)
141+
142+
143+
def main():
144+
parser = argparse.ArgumentParser(description='__doc__')
145+
parser.add_argument('oldrunid', help='Path to old run idir')
146+
parser.add_argument('newrunid', help='Local to new run dir')
147+
parser.add_argument('--local', action='store_true')
148+
args = parser.parse_args()
149+
compare_runs(args.oldrunid, args.newrunid)
150+
151+
152+
if __name__ == '__main__':
153+
main()

0 commit comments

Comments
 (0)