123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166 |
- #!/usr/bin/env python
- # Copyright 2016 Google Inc.
- #
- # Use of this source code is governed by a BSD-style license that can be
- # found in the LICENSE file.
- from __future__ import print_function
- from _benchresult import BenchResult
- from argparse import ArgumentParser
- from collections import defaultdict, namedtuple
- from datetime import datetime
- import operator
- import os
- import sys
- import tempfile
- import urllib
- import urlparse
- import webbrowser
- __argparse = ArgumentParser(description="""
- Formats skpbench.py outputs as csv.
- This script can also be used to generate a Google sheet:
- (1) Install the "Office Editing for Docs, Sheets & Slides" Chrome extension:
- https://chrome.google.com/webstore/detail/office-editing-for-docs-s/gbkeegbaiigmenfmjfclcdgdpimamgkj
- (2) Update your global OS file associations to use Chrome for .csv files.
- (3) Run parseskpbench.py with the --open flag.
- """)
- __argparse.add_argument('-r', '--result',
- choices=['accum', 'median', 'max', 'min'], default='accum',
- help="result to use for cell values")
- __argparse.add_argument('-f', '--force',
- action='store_true', help='silently ignore warnings')
- __argparse.add_argument('-o', '--open',
- action='store_true',
- help="generate a temp file and open it (theoretically in a web browser)")
- __argparse.add_argument('-n', '--name',
- default='skpbench_%s' % datetime.now().strftime('%Y-%m-%d_%H.%M.%S.csv'),
- help="if using --open, a name for the temp file")
- __argparse.add_argument('sources',
- nargs='+', help="source files that contain skpbench results ('-' for stdin)")
- FLAGS = __argparse.parse_args()
- RESULT_QUALIFIERS = ('sample_ms', 'clock', 'metric')
- class FullConfig(namedtuple('fullconfig', ('config',) + RESULT_QUALIFIERS)):
- def qualified_name(self, qualifiers=RESULT_QUALIFIERS):
- return get_qualified_name(self.config.replace(',', ' '),
- {x:getattr(self, x) for x in qualifiers})
- def get_qualified_name(name, qualifiers):
- if not qualifiers:
- return name
- else:
- args = ('%s=%s' % (k,v) for k,v in qualifiers.iteritems())
- return '%s (%s)' % (name, ' '.join(args))
- class Parser:
- def __init__(self):
- self.sheet_qualifiers = {x:None for x in RESULT_QUALIFIERS}
- self.config_qualifiers = set()
- self.fullconfigs = list() # use list to preserve the order.
- self.rows = defaultdict(dict)
- self.cols = defaultdict(dict)
- def parse_file(self, infile):
- for line in infile:
- match = BenchResult.match(line)
- if not match:
- continue
- fullconfig = FullConfig(*(match.get_string(x)
- for x in FullConfig._fields))
- if not fullconfig in self.fullconfigs:
- self.fullconfigs.append(fullconfig)
- for qualifier, value in self.sheet_qualifiers.items():
- if value is None:
- self.sheet_qualifiers[qualifier] = match.get_string(qualifier)
- elif value != match.get_string(qualifier):
- del self.sheet_qualifiers[qualifier]
- self.config_qualifiers.add(qualifier)
- self.rows[match.bench][fullconfig] = match.get_string(FLAGS.result)
- self.cols[fullconfig][match.bench] = getattr(match, FLAGS.result)
- def print_csv(self, outfile=sys.stdout):
- # Write the title.
- print(get_qualified_name(FLAGS.result, self.sheet_qualifiers), file=outfile)
- # Write the header.
- outfile.write('bench,')
- for fullconfig in self.fullconfigs:
- outfile.write('%s,' % fullconfig.qualified_name(self.config_qualifiers))
- outfile.write('\n')
- # Write the rows.
- for bench, row in self.rows.iteritems():
- outfile.write('%s,' % bench)
- for fullconfig in self.fullconfigs:
- if fullconfig in row:
- outfile.write('%s,' % row[fullconfig])
- elif FLAGS.force:
- outfile.write('NULL,')
- else:
- raise ValueError("%s: missing value for %s. (use --force to ignore)" %
- (bench,
- fullconfig.qualified_name(self.config_qualifiers)))
- outfile.write('\n')
- # Add simple, literal averages.
- if len(self.rows) > 1:
- outfile.write('\n')
- self._print_computed_row('MEAN',
- lambda col: reduce(operator.add, col.values()) / len(col),
- outfile=outfile)
- self._print_computed_row('GEOMEAN',
- lambda col: reduce(operator.mul, col.values()) ** (1.0 / len(col)),
- outfile=outfile)
- def _print_computed_row(self, name, func, outfile=sys.stdout):
- outfile.write('%s,' % name)
- for fullconfig in self.fullconfigs:
- if len(self.cols[fullconfig]) != len(self.rows):
- outfile.write('NULL,')
- continue
- outfile.write('%.4g,' % func(self.cols[fullconfig]))
- outfile.write('\n')
- def main():
- parser = Parser()
- # Parse the input files.
- for src in FLAGS.sources:
- if src == '-':
- parser.parse_file(sys.stdin)
- else:
- with open(src, mode='r') as infile:
- parser.parse_file(infile)
- # Print the csv.
- if not FLAGS.open:
- parser.print_csv()
- else:
- dirname = tempfile.mkdtemp()
- basename = FLAGS.name
- if os.path.splitext(basename)[1] != '.csv':
- basename += '.csv';
- pathname = os.path.join(dirname, basename)
- with open(pathname, mode='w') as tmpfile:
- parser.print_csv(outfile=tmpfile)
- fileuri = urlparse.urljoin('file:', urllib.pathname2url(pathname))
- print('opening %s' % fileuri)
- webbrowser.open(fileuri)
- if __name__ == '__main__':
- main()
|