blob: f903ec00a885aae8e4e97168dd66db8f523086f0 [file] [log] [blame]
csmartdalton4b5179b2016-09-19 11:03:58 -07001#!/usr/bin/env python
2
3# Copyright 2016 Google Inc.
4#
5# Use of this source code is governed by a BSD-style license that can be
6# found in the LICENSE file.
7
8from __future__ import print_function
9from _benchresult import BenchResult
10from argparse import ArgumentParser
11from datetime import datetime
12import collections
13import operator
14import os
15import sys
16import tempfile
17import urllib
18import urlparse
19import webbrowser
20
csmartdaltonbf41fa82016-09-23 11:36:11 -070021__argparse = ArgumentParser(description="""
csmartdalton4b5179b2016-09-19 11:03:58 -070022
23Parses output files from skpbench.py into csv.
24
25This script can also be used to generate a Google sheet:
26
27(1) Install the "Office Editing for Docs, Sheets & Slides" Chrome extension:
28 https://chrome.google.com/webstore/detail/office-editing-for-docs-s/gbkeegbaiigmenfmjfclcdgdpimamgkj
29
30(2) Designate Chrome os-wide as the default application for opening .csv files.
31
32(3) Run parseskpbench.py with the --open flag.
33
csmartdaltonbf41fa82016-09-23 11:36:11 -070034""")
csmartdalton4b5179b2016-09-19 11:03:58 -070035
36__argparse.add_argument('-r', '--result',
37 choices=['median', 'accum', 'max', 'min'], default='median',
csmartdaltond7a9db62016-09-22 05:10:02 -070038 help="result to use for cell values")
csmartdalton4b5179b2016-09-19 11:03:58 -070039__argparse.add_argument('-f', '--force',
40 action='store_true', help='silently ignore warnings')
41__argparse.add_argument('-o', '--open',
42 action='store_true',
csmartdaltond7a9db62016-09-22 05:10:02 -070043 help="generate a temp file and open it (theoretically in a web browser)")
csmartdalton4b5179b2016-09-19 11:03:58 -070044__argparse.add_argument('-n', '--name',
45 default='skpbench_%s' % datetime.now().strftime('%Y-%m-%d_%H.%M.%S.csv'),
csmartdaltond7a9db62016-09-22 05:10:02 -070046 help="if using --open, a name for the temp file")
csmartdalton4b5179b2016-09-19 11:03:58 -070047__argparse.add_argument('sources',
csmartdaltond7a9db62016-09-22 05:10:02 -070048 nargs='+', help="source files with skpbench results ('-' for stdin)")
csmartdalton4b5179b2016-09-19 11:03:58 -070049
50FLAGS = __argparse.parse_args()
51
52
53class Parser:
54 def __init__(self):
55 self.configs = list() # use list to preserve the order configs appear in.
56 self.rows = collections.defaultdict(dict)
57 self.cols = collections.defaultdict(dict)
58 self.metric = None
59 self.samples = None
60 self.sample_ms = None
61
62 def parse_file(self, infile):
63 for line in infile:
64 match = BenchResult.match(line)
65 if not match:
66 continue
67 if self.metric is None:
68 self.metric = match.metric
69 elif match.metric != self.metric:
csmartdaltond7a9db62016-09-22 05:10:02 -070070 raise ValueError("results have mismatched metrics (%s and %s)" %
csmartdalton4b5179b2016-09-19 11:03:58 -070071 (self.metric, match.metric))
72 if self.samples is None:
73 self.samples = match.samples
74 elif not FLAGS.force and match.samples != self.samples:
csmartdaltond7a9db62016-09-22 05:10:02 -070075 raise ValueError("results have mismatched number of samples. "
76 "(use --force to ignore)")
csmartdalton4b5179b2016-09-19 11:03:58 -070077 if self.sample_ms is None:
78 self.sample_ms = match.sample_ms
79 elif not FLAGS.force and match.sample_ms != self.sample_ms:
csmartdaltond7a9db62016-09-22 05:10:02 -070080 raise ValueError("results have mismatched sampling times. "
81 "(use --force to ignore)")
csmartdalton4b5179b2016-09-19 11:03:58 -070082 if not match.config in self.configs:
83 self.configs.append(match.config)
84 self.rows[match.bench][match.config] = match.get_string(FLAGS.result)
85 self.cols[match.config][match.bench] = getattr(match, FLAGS.result)
86
87 def print_csv(self, outfile=sys.stdout):
88 print('%s_%s' % (FLAGS.result, self.metric), file=outfile)
89
90 # Write the header.
91 outfile.write('bench,')
92 for config in self.configs:
93 outfile.write('%s,' % config)
94 outfile.write('\n')
95
96 # Write the rows.
97 for bench, row in self.rows.items():
98 outfile.write('%s,' % bench)
99 for config in self.configs:
100 if config in row:
101 outfile.write('%s,' % row[config])
102 elif FLAGS.force:
103 outfile.write(',')
104 else:
csmartdaltond7a9db62016-09-22 05:10:02 -0700105 raise ValueError("%s: missing value for %s. (use --force to ignore)" %
csmartdalton4b5179b2016-09-19 11:03:58 -0700106 (bench, config))
107 outfile.write('\n')
108
109 # Add simple, literal averages.
110 if len(self.rows) > 1:
111 outfile.write('\n')
112 self.__print_computed_row('MEAN',
113 lambda col: reduce(operator.add, col.values()) / len(col),
114 outfile=outfile)
115 self.__print_computed_row('GEOMEAN',
116 lambda col: reduce(operator.mul, col.values()) ** (1.0 / len(col)),
117 outfile=outfile)
118
119 def __print_computed_row(self, name, func, outfile=sys.stdout):
120 outfile.write('%s,' % name)
121 for config in self.configs:
122 assert(len(self.cols[config]) == len(self.rows))
123 outfile.write('%.4g,' % func(self.cols[config]))
124 outfile.write('\n')
125
126
127def main():
128 parser = Parser()
129
130 # Parse the input files.
131 for src in FLAGS.sources:
132 if src == '-':
133 parser.parse_file(sys.stdin)
134 else:
135 with open(src, mode='r') as infile:
136 parser.parse_file(infile)
137
138 # Print the csv.
139 if not FLAGS.open:
140 parser.print_csv()
141 else:
142 dirname = tempfile.mkdtemp()
143 basename = FLAGS.name
144 if os.path.splitext(basename)[1] != '.csv':
145 basename += '.csv';
146 pathname = os.path.join(dirname, basename)
147 with open(pathname, mode='w') as tmpfile:
148 parser.print_csv(outfile=tmpfile)
149 fileuri = urlparse.urljoin('file:', urllib.pathname2url(pathname))
150 print('opening %s' % fileuri)
151 webbrowser.open(fileuri)
152
153
154if __name__ == '__main__':
155 main()