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
mtklein56df2de2016-10-04 12:49:45 -070012import collections
csmartdalton4b5179b2016-09-19 11:03:58 -070013import 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
mtklein56df2de2016-10-04 12:49:45 -070030(2) Designate Chrome os-wide as the default application for opening .csv files.
csmartdalton4b5179b2016-09-19 11:03:58 -070031
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):
mtklein56df2de2016-10-04 12:49:45 -070055 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.sample_ms = None
csmartdalton4b5179b2016-09-19 11:03:58 -070060
61 def parse_file(self, infile):
62 for line in infile:
63 match = BenchResult.match(line)
64 if not match:
65 continue
mtklein56df2de2016-10-04 12:49:45 -070066 if self.metric is None:
67 self.metric = match.metric
68 elif match.metric != self.metric:
69 raise ValueError("results have mismatched metrics (%s and %s)" %
70 (self.metric, match.metric))
71 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:
74 raise ValueError("results have mismatched sampling times. "
75 "(use --force to ignore)")
76 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)
csmartdalton4b5179b2016-09-19 11:03:58 -070080
81 def print_csv(self, outfile=sys.stdout):
mtklein56df2de2016-10-04 12:49:45 -070082 print('%s_%s' % (FLAGS.result, self.metric), file=outfile)
csmartdalton4b5179b2016-09-19 11:03:58 -070083
84 # Write the header.
85 outfile.write('bench,')
mtklein56df2de2016-10-04 12:49:45 -070086 for config in self.configs:
87 outfile.write('%s,' % config)
csmartdalton4b5179b2016-09-19 11:03:58 -070088 outfile.write('\n')
89
90 # Write the rows.
mtklein56df2de2016-10-04 12:49:45 -070091 for bench, row in self.rows.items():
csmartdalton4b5179b2016-09-19 11:03:58 -070092 outfile.write('%s,' % bench)
mtklein56df2de2016-10-04 12:49:45 -070093 for config in self.configs:
94 if config in row:
95 outfile.write('%s,' % row[config])
csmartdalton4b5179b2016-09-19 11:03:58 -070096 elif FLAGS.force:
mtklein56df2de2016-10-04 12:49:45 -070097 outfile.write(',')
csmartdalton4b5179b2016-09-19 11:03:58 -070098 else:
csmartdaltond7a9db62016-09-22 05:10:02 -070099 raise ValueError("%s: missing value for %s. (use --force to ignore)" %
mtklein56df2de2016-10-04 12:49:45 -0700100 (bench, config))
csmartdalton4b5179b2016-09-19 11:03:58 -0700101 outfile.write('\n')
102
103 # Add simple, literal averages.
104 if len(self.rows) > 1:
105 outfile.write('\n')
mtklein56df2de2016-10-04 12:49:45 -0700106 self.__print_computed_row('MEAN',
csmartdalton4b5179b2016-09-19 11:03:58 -0700107 lambda col: reduce(operator.add, col.values()) / len(col),
108 outfile=outfile)
mtklein56df2de2016-10-04 12:49:45 -0700109 self.__print_computed_row('GEOMEAN',
csmartdalton4b5179b2016-09-19 11:03:58 -0700110 lambda col: reduce(operator.mul, col.values()) ** (1.0 / len(col)),
111 outfile=outfile)
112
mtklein56df2de2016-10-04 12:49:45 -0700113 def __print_computed_row(self, name, func, outfile=sys.stdout):
csmartdalton4b5179b2016-09-19 11:03:58 -0700114 outfile.write('%s,' % name)
mtklein56df2de2016-10-04 12:49:45 -0700115 for config in self.configs:
116 assert(len(self.cols[config]) == len(self.rows))
117 outfile.write('%.4g,' % func(self.cols[config]))
csmartdalton4b5179b2016-09-19 11:03:58 -0700118 outfile.write('\n')
119
mtklein56df2de2016-10-04 12:49:45 -0700120
csmartdalton4b5179b2016-09-19 11:03:58 -0700121def 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()