blob: 5fe146ee0917474331f24f9964c437088a4e5157 [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',
csmartdalton6904b192016-09-29 06:23:23 -070037 choices=['accum', 'median', 'max', 'min'], default='accum',
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
csmartdalton4b5179b2016-09-19 11:03:58 -070059 self.sample_ms = None
60
61 def parse_file(self, infile):
62 for line in infile:
63 match = BenchResult.match(line)
64 if not match:
65 continue
66 if self.metric is None:
67 self.metric = match.metric
68 elif match.metric != self.metric:
csmartdaltond7a9db62016-09-22 05:10:02 -070069 raise ValueError("results have mismatched metrics (%s and %s)" %
csmartdalton4b5179b2016-09-19 11:03:58 -070070 (self.metric, match.metric))
csmartdalton4b5179b2016-09-19 11:03:58 -070071 if self.sample_ms is None:
72 self.sample_ms = match.sample_ms
73 elif not FLAGS.force and match.sample_ms != self.sample_ms:
csmartdaltond7a9db62016-09-22 05:10:02 -070074 raise ValueError("results have mismatched sampling times. "
75 "(use --force to ignore)")
csmartdalton4b5179b2016-09-19 11:03:58 -070076 if not match.config in self.configs:
77 self.configs.append(match.config)
78 self.rows[match.bench][match.config] = match.get_string(FLAGS.result)
79 self.cols[match.config][match.bench] = getattr(match, FLAGS.result)
80
81 def print_csv(self, outfile=sys.stdout):
82 print('%s_%s' % (FLAGS.result, self.metric), file=outfile)
83
84 # Write the header.
85 outfile.write('bench,')
86 for config in self.configs:
87 outfile.write('%s,' % config)
88 outfile.write('\n')
89
90 # Write the rows.
91 for bench, row in self.rows.items():
92 outfile.write('%s,' % bench)
93 for config in self.configs:
94 if config in row:
95 outfile.write('%s,' % row[config])
96 elif FLAGS.force:
97 outfile.write(',')
98 else:
csmartdaltond7a9db62016-09-22 05:10:02 -070099 raise ValueError("%s: missing value for %s. (use --force to ignore)" %
csmartdalton4b5179b2016-09-19 11:03:58 -0700100 (bench, config))
101 outfile.write('\n')
102
103 # Add simple, literal averages.
104 if len(self.rows) > 1:
105 outfile.write('\n')
106 self.__print_computed_row('MEAN',
107 lambda col: reduce(operator.add, col.values()) / len(col),
108 outfile=outfile)
109 self.__print_computed_row('GEOMEAN',
110 lambda col: reduce(operator.mul, col.values()) ** (1.0 / len(col)),
111 outfile=outfile)
112
113 def __print_computed_row(self, name, func, outfile=sys.stdout):
114 outfile.write('%s,' % name)
115 for config in self.configs:
116 assert(len(self.cols[config]) == len(self.rows))
117 outfile.write('%.4g,' % func(self.cols[config]))
118 outfile.write('\n')
119
120
121def main():
122 parser = Parser()
123
124 # Parse the input files.
125 for src in FLAGS.sources:
126 if src == '-':
127 parser.parse_file(sys.stdin)
128 else:
129 with open(src, mode='r') as infile:
130 parser.parse_file(infile)
131
132 # Print the csv.
133 if not FLAGS.open:
134 parser.print_csv()
135 else:
136 dirname = tempfile.mkdtemp()
137 basename = FLAGS.name
138 if os.path.splitext(basename)[1] != '.csv':
139 basename += '.csv';
140 pathname = os.path.join(dirname, basename)
141 with open(pathname, mode='w') as tmpfile:
142 parser.print_csv(outfile=tmpfile)
143 fileuri = urlparse.urljoin('file:', urllib.pathname2url(pathname))
144 print('opening %s' % fileuri)
145 webbrowser.open(fileuri)
146
147
148if __name__ == '__main__':
149 main()