Home | History | Annotate | Download | only in bench
      1 #!/usr/bin/env python
      2 # -*- coding: ascii -*-
      3 r"""
      4 =========================
      5  Write benchmark results
      6 =========================
      7 
      8 Write benchmark results.
      9 
     10 :Copyright:
     11 
     12  Copyright 2014
     13  Andr\xe9 Malo or his licensors, as applicable
     14 
     15 :License:
     16 
     17  Licensed under the Apache License, Version 2.0 (the "License");
     18  you may not use this file except in compliance with the License.
     19  You may obtain a copy of the License at
     20 
     21      http://www.apache.org/licenses/LICENSE-2.0
     22 
     23  Unless required by applicable law or agreed to in writing, software
     24  distributed under the License is distributed on an "AS IS" BASIS,
     25  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     26  See the License for the specific language governing permissions and
     27  limitations under the License.
     28 
     29 Usage::
     30 
     31     python -mbench.write [-p plain] [-t table] <pickled
     32 
     33     -p plain  Plain file to write to (like docs/BENCHMARKS).
     34     -t table  Table file to write to (like docs/_userdoc/benchmark.txt).
     35 
     36 """
     37 if __doc__:
     38     __doc__ = __doc__.encode('ascii').decode('unicode_escape')
     39 __author__ = r"Andr\xe9 Malo".encode('ascii').decode('unicode_escape')
     40 __docformat__ = "restructuredtext en"
     41 __license__ = "Apache License, Version 2.0"
     42 __version__ = "1.0.0"
     43 
     44 import os as _os
     45 import re as _re
     46 import sys as _sys
     47 
     48 
     49 try:
     50     unicode
     51 except NameError:
     52     def uni(v):
     53         if hasattr(v, 'decode'):
     54             return v.decode('latin-1')
     55         return str(v)
     56 else:
     57     def uni(v):
     58         if isinstance(v, unicode):
     59             return v.encode('utf-8')
     60         return str(v)
     61 
     62 
     63 def write_table(filename, results):
     64     """
     65     Output tabled benchmark results
     66 
     67     :Parameters:
     68       `filename` : ``str``
     69         Filename to write to
     70 
     71       `results` : ``list``
     72         Results
     73     """
     74     try:
     75         next
     76     except NameError:
     77         next = lambda i: (getattr(i, 'next', None) or i.__next__)()
     78     try:
     79         cmp
     80     except NameError:
     81         cmp = lambda a, b: (a > b) - (a < b)
     82 
     83     names = [
     84         ('cssmin', 'YUI Port'),
     85         ('rcssmin', '|rcssmin|'),
     86         ('_rcssmin', r'_\ |rcssmin|'),
     87     ]
     88     benched_per_table = 2
     89 
     90     results = sorted(results, reverse=True)
     91 
     92     # First we transform our data into a table (list of lists)
     93     pythons, widths = [], [0] * (benched_per_table + 1)
     94     last_version = None
     95     for version, _, result in results:
     96         version = uni(version)
     97         if not(last_version is None or version.startswith('2.')):
     98             continue
     99         last_version = version
    100 
    101         namesub = _re.compile(r'(?:-\d+(?:\.\d+)*)?\.css$').sub
    102         result = iter(result)
    103         tables = []
    104 
    105         # given our data it's easier to create the table transposed...
    106         for benched in result:
    107             rows = [['Name'] + [desc for _, desc in names]]
    108             for _ in range(benched_per_table):
    109                 if _:
    110                     try:
    111                         benched = next(result)
    112                     except StopIteration:
    113                         rows.append([''] + ['' for _ in names])
    114                         continue
    115 
    116                 times = dict((
    117                     uni(port), (time, benched['sizes'][idx])
    118                 ) for idx, (port, time) in enumerate(benched['times']))
    119                 columns = ['%s (%.1f)' % (
    120                     namesub('', _os.path.basename(uni(benched['filename']))),
    121                     benched['size'] / 1024.0,
    122                 )]
    123                 for idx, (port, _) in enumerate(names):
    124                     if port not in times:
    125                         columns.append('n/a')
    126                         continue
    127                     time, size = times[port]
    128                     if time is None:
    129                         columns.append('(failed)')
    130                         continue
    131                     columns.append('%s%.2f ms (%.1f %s)' % (
    132                         idx == 0 and ' ' or '',
    133                         time,
    134                         size / 1024.0,
    135                         idx == 0 and '\\*' or ['=', '>', '<'][
    136                             cmp(size, benched['sizes'][0])
    137                         ],
    138                     ))
    139                 rows.append(columns)
    140 
    141             # calculate column widths (global for all tables)
    142             for idx, row in enumerate(rows):
    143                 widths[idx] = max(widths[idx], max(map(len, row)))
    144 
    145             # ... and transpose it back.
    146             tables.append(zip(*rows))
    147         pythons.append((version, tables))
    148 
    149         if last_version.startswith('2.'):
    150             break
    151 
    152     # Second we create a rest table from it
    153     lines = []
    154     separator = lambda c='-': '+'.join([''] + [
    155         c * (width + 2) for width in widths
    156     ] + [''])
    157 
    158     for idx, (version, tables) in enumerate(pythons):
    159         if idx:
    160             lines.append('')
    161             lines.append('')
    162 
    163         line = 'Python %s' % (version,)
    164         lines.append(line)
    165         lines.append('~' * len(line))
    166 
    167         for table in tables:
    168             lines.append('')
    169             lines.append('.. rst-class:: benchmark')
    170             lines.append('')
    171 
    172             for idx, row in enumerate(table):
    173                 if idx == 0:
    174                     # header
    175                     lines.append(separator())
    176                     lines.append('|'.join([''] + [
    177                         ' %s%*s ' % (col, len(col) - width, '')
    178                         for width, col in zip(widths, row)
    179                     ] + ['']))
    180                     lines.append(separator('='))
    181                 else: # data
    182                     lines.append('|'.join([''] + [
    183                         j == 0 and (
    184                             ' %s%*s ' % (col, len(col) - widths[j], '')
    185                         ) or (
    186                             ['%*s  ', ' %*s '][idx == 1] % (widths[j], col)
    187                         )
    188                         for j, col in enumerate(row)
    189                     ] + ['']))
    190                     lines.append(separator())
    191 
    192     fplines = []
    193     fp = open(filename)
    194     try:
    195         fpiter = iter(fp)
    196         for line in fpiter:
    197             line = line.rstrip()
    198             if line == '.. begin tables':
    199                 buf = []
    200                 for line in fpiter:
    201                     line = line.rstrip()
    202                     if line == '.. end tables':
    203                         fplines.append('.. begin tables')
    204                         fplines.append('')
    205                         fplines.extend(lines)
    206                         fplines.append('')
    207                         fplines.append('.. end tables')
    208                         buf = []
    209                         break
    210                     else:
    211                         buf.append(line)
    212                 else:
    213                     fplines.extend(buf)
    214                     _sys.stderr.write("Placeholder container not found!\n")
    215             else:
    216                 fplines.append(line)
    217     finally:
    218         fp.close()
    219 
    220     fp = open(filename, 'w')
    221     try:
    222         fp.write('\n'.join(fplines) + '\n')
    223     finally:
    224         fp.close()
    225 
    226 
    227 def write_plain(filename, results):
    228     """
    229     Output plain benchmark results
    230 
    231     :Parameters:
    232       `filename` : ``str``
    233         Filename to write to
    234 
    235       `results` : ``list``
    236         Results
    237     """
    238     lines = []
    239     results = sorted(results, reverse=True)
    240     for idx, (version, import_notes, result) in enumerate(results):
    241         if idx:
    242             lines.append('')
    243             lines.append('')
    244 
    245         lines.append('$ python%s -OO bench/main.py bench/*.css' % (
    246             '.'.join(version.split('.')[:2])
    247         ))
    248         lines.append('~' * 72)
    249         for note in import_notes:
    250             lines.append(uni(note))
    251         lines.append('Python Release: %s' % (version,))
    252 
    253         for single in result:
    254             lines.append('')
    255             lines.append('Benchmarking %r... (%.1f KiB)' % (
    256                 uni(single['filename']), single['size'] / 1024.0
    257             ))
    258             for msg in single['messages']:
    259                 lines.append(msg)
    260             times = []
    261             space = max([len(uni(port)) for port, _ in single['times']])
    262             for idx, (port, time) in enumerate(single['times']):
    263                 port = uni(port)
    264                 if time is None:
    265                     lines.append("  FAILED %s" % (port,))
    266                 else:
    267                     times.append(time)
    268                     lines.append(
    269                         "  Timing %s%s ... (%5.1f KiB %s) %8.2f ms" % (
    270                             port,
    271                             " " * (space - len(port)),
    272                             single['sizes'][idx] / 1024.0,
    273                             idx == 0 and '*' or ['=', '>', '<'][
    274                                 cmp(single['sizes'][idx], single['sizes'][0])
    275                             ],
    276                             time
    277                         )
    278                     )
    279                     if len(times) > 1:
    280                         lines[-1] += " (factor: %s)" % (', '.join([
    281                             '%.2f' % (timed / time) for timed in times[:-1]
    282                         ]))
    283 
    284     lines.append('')
    285     lines.append('')
    286     lines.append('# vim: nowrap')
    287     fp = open(filename, 'w')
    288     try:
    289         fp.write('\n'.join(lines) + '\n')
    290     finally:
    291         fp.close()
    292 
    293 
    294 def main(argv=None):
    295     """ Main """
    296     import getopt as _getopt
    297     import pickle as _pickle
    298 
    299     if argv is None:
    300         argv = _sys.argv[1:]
    301     try:
    302         opts, args = _getopt.getopt(argv, "hp:t:", ["help"])
    303     except getopt.GetoptError:
    304         e = _sys.exc_info()[0](_sys.exc_info()[1])
    305         print >> _sys.stderr, "%s\nTry %s -mbench.write --help" % (
    306             e,
    307             _os.path.basename(_sys.executable),
    308         )
    309         _sys.exit(2)
    310 
    311     plain, table = None, None
    312     for key, value in opts:
    313         if key in ("-h", "--help"):
    314             print >> _sys.stderr, (
    315                 "%s -mbench.write [-p plain] [-t table] <pickled" % (
    316                     _os.path.basename(_sys.executable),
    317                 )
    318             )
    319             _sys.exit(0)
    320         elif key == '-p':
    321             plain = str(value)
    322         elif key == '-t':
    323             table = str(value)
    324 
    325     struct = []
    326     _sys.stdin = getattr(_sys.stdin, 'detach', lambda: _sys.stdin)()
    327     try:
    328         while True:
    329             version, import_notes, result = _pickle.load(_sys.stdin)
    330             if hasattr(version, 'decode'):
    331                 version = version.decode('latin-1')
    332             struct.append((version, import_notes, result))
    333     except EOFError:
    334         pass
    335 
    336     if plain:
    337         write_plain(plain, struct)
    338 
    339     if table:
    340         write_table(table, struct)
    341 
    342 
    343 if __name__ == '__main__':
    344     main()
    345