• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2# -*- coding: ascii -*-
3r"""
4=========================
5 Write benchmark results
6=========================
7
8Write benchmark results.
9
10:Copyright:
11
12 Copyright 2014 - 2015
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
29Usage::
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"""
37if __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
44import os as _os
45import re as _re
46import sys as _sys
47
48
49try:
50    unicode
51except NameError:
52    def uni(v):
53        if hasattr(v, 'decode'):
54            return v.decode('latin-1')
55        return str(v)
56else:
57    def uni(v):
58        if isinstance(v, unicode):
59            return v.encode('utf-8')
60        return str(v)
61
62
63def 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        ('simple_port', 'Simple Port'),
85        ('jsmin_2_0_9', 'jsmin 2.0.9'),
86        ('rjsmin', '|rjsmin|'),
87        ('_rjsmin', r'_\ |rjsmin|'),
88    ]
89    benched_per_table = 2
90
91    results = sorted(results, reverse=True)
92
93    # First we transform our data into a table (list of lists)
94    pythons, widths = [], [0] * (benched_per_table + 1)
95    last_version = None
96    for version, _, result in results:
97        version = uni(version)
98        if not(last_version is None or version.startswith('2.')):
99            continue
100        last_version = version
101
102        namesub = _re.compile(r'(?:-\d+(?:\.\d+)*)?\.js$').sub
103        result = iter(result)
104        tables = []
105
106        # given our data it's easier to create the table transposed...
107        for benched in result:
108            rows = [['Name'] + [desc for _, desc in names]]
109            for _ in range(benched_per_table):
110                if _:
111                    try:
112                        benched = next(result)
113                    except StopIteration:
114                        rows.append([''] + ['' for _ in names])
115                        continue
116
117                times = dict((
118                    uni(port), (time, benched['sizes'][idx])
119                ) for idx, (port, time) in enumerate(benched['times']))
120                columns = ['%s (%.1f)' % (
121                    namesub('', _os.path.basename(uni(benched['filename']))),
122                    benched['size'] / 1024.0,
123                )]
124                for idx, (port, _) in enumerate(names):
125                    if port not in times:
126                        columns.append('n/a')
127                        continue
128                    time, size = times[port]
129                    if time is None:
130                        columns.append('(failed)')
131                        continue
132                    columns.append('%s%.2f ms (%.1f %s)' % (
133                        idx == 0 and ' ' or '',
134                        time,
135                        size / 1024.0,
136                        idx == 0 and '\\*' or ['=', '>', '<'][
137                            cmp(size, benched['sizes'][0])
138                        ],
139                    ))
140                rows.append(columns)
141
142            # calculate column widths (global for all tables)
143            for idx, row in enumerate(rows):
144                widths[idx] = max(widths[idx], max(map(len, row)))
145
146            # ... and transpose it back.
147            tables.append(zip(*rows))
148        pythons.append((version, tables))
149
150        if last_version.startswith('2.'):
151            break
152
153    # Second we create a rest table from it
154    lines = []
155    separator = lambda c='-': '+'.join([''] + [
156        c * (width + 2) for width in widths
157    ] + [''])
158
159    for idx, (version, tables) in enumerate(pythons):
160        if idx:
161            lines.append('')
162            lines.append('')
163
164        line = 'Python %s' % (version,)
165        lines.append(line)
166        lines.append('~' * len(line))
167
168        for table in tables:
169            lines.append('')
170            lines.append('.. rst-class:: benchmark')
171            lines.append('')
172
173            for idx, row in enumerate(table):
174                if idx == 0:
175                    # header
176                    lines.append(separator())
177                    lines.append('|'.join([''] + [
178                        ' %s%*s ' % (col, len(col) - width, '')
179                        for width, col in zip(widths, row)
180                    ] + ['']))
181                    lines.append(separator('='))
182                else: # data
183                    lines.append('|'.join([''] + [
184                        j == 0 and (
185                            ' %s%*s ' % (col, len(col) - widths[j], '')
186                        ) or (
187                            ['%*s  ', ' %*s '][idx == 1] % (widths[j], col)
188                        )
189                        for j, col in enumerate(row)
190                    ] + ['']))
191                    lines.append(separator())
192
193    fplines = []
194    fp = open(filename)
195    try:
196        fpiter = iter(fp)
197        for line in fpiter:
198            line = line.rstrip()
199            if line == '.. begin tables':
200                buf = []
201                for line in fpiter:
202                    line = line.rstrip()
203                    if line == '.. end tables':
204                        fplines.append('.. begin tables')
205                        fplines.append('')
206                        fplines.extend(lines)
207                        fplines.append('')
208                        fplines.append('.. end tables')
209                        buf = []
210                        break
211                    else:
212                        buf.append(line)
213                else:
214                    fplines.extend(buf)
215                    _sys.stderr.write("Placeholder container not found!\n")
216            else:
217                fplines.append(line)
218    finally:
219        fp.close()
220
221    fp = open(filename, 'w')
222    try:
223        fp.write('\n'.join(fplines) + '\n')
224    finally:
225        fp.close()
226
227
228def write_plain(filename, results):
229    """
230    Output plain benchmark results
231
232    :Parameters:
233      `filename` : ``str``
234        Filename to write to
235
236      `results` : ``list``
237        Results
238    """
239    lines = []
240    results = sorted(results, reverse=True)
241    for idx, (version, import_notes, result) in enumerate(results):
242        if idx:
243            lines.append('')
244            lines.append('')
245
246        lines.append('$ python%s -OO bench/main.py bench/*.js' % (
247            '.'.join(version.split('.')[:2])
248        ))
249        lines.append('~' * 72)
250        for note in import_notes:
251            lines.append(uni(note))
252        lines.append('Python Release: %s' % (version,))
253
254        for single in result:
255            lines.append('')
256            lines.append('Benchmarking %r... (%.1f KiB)' % (
257                uni(single['filename']), single['size'] / 1024.0
258            ))
259            for msg in single['messages']:
260                lines.append(msg)
261            times = []
262            space = max([len(uni(port)) for port, _ in single['times']])
263            for idx, (port, time) in enumerate(single['times']):
264                port = uni(port)
265                if time is None:
266                    lines.append("  FAILED %s" % (port,))
267                else:
268                    times.append(time)
269                    lines.append(
270                        "  Timing %s%s ... (%5.1f KiB %s) %8.2f ms" % (
271                            port,
272                            " " * (space - len(port)),
273                            single['sizes'][idx] / 1024.0,
274                            idx == 0 and '*' or ['=', '>', '<'][
275                                cmp(single['sizes'][idx], single['sizes'][0])
276                            ],
277                            time
278                        )
279                    )
280                    if len(times) > 1:
281                        lines[-1] += " (factor: %s)" % (', '.join([
282                            '%.2f' % (timed / time) for timed in times[:-1]
283                        ]))
284
285    lines.append('')
286    lines.append('')
287    lines.append('# vim: nowrap')
288    fp = open(filename, 'w')
289    try:
290        fp.write('\n'.join(lines) + '\n')
291    finally:
292        fp.close()
293
294
295def main(argv=None):
296    """ Main """
297    import getopt as _getopt
298    import pickle as _pickle
299
300    if argv is None:
301        argv = _sys.argv[1:]
302    try:
303        opts, args = _getopt.getopt(argv, "hp:t:", ["help"])
304    except getopt.GetoptError:
305        e = _sys.exc_info()[0](_sys.exc_info()[1])
306        print >> _sys.stderr, "%s\nTry %s -mbench.write --help" % (
307            e,
308            _os.path.basename(_sys.executable),
309        )
310        _sys.exit(2)
311
312    plain, table = None, None
313    for key, value in opts:
314        if key in ("-h", "--help"):
315            print >> _sys.stderr, (
316                "%s -mbench.write [-p plain] [-t table] <pickled" % (
317                    _os.path.basename(_sys.executable),
318                )
319            )
320            _sys.exit(0)
321        elif key == '-p':
322            plain = str(value)
323        elif key == '-t':
324            table = str(value)
325
326    struct = []
327    _sys.stdin = getattr(_sys.stdin, 'detach', lambda: _sys.stdin)()
328    try:
329        while True:
330            version, import_notes, result = _pickle.load(_sys.stdin)
331            if hasattr(version, 'decode'):
332                version = version.decode('latin-1')
333            struct.append((version, import_notes, result))
334    except EOFError:
335        pass
336
337    if plain:
338        write_plain(plain, struct)
339
340    if table:
341        write_table(table, struct)
342
343
344if __name__ == '__main__':
345    main()
346