1#!/usr/bin/env python
2# -*- coding: ascii -*-
3r"""
4=================================
5 Benchmark jsmin implementations
6=================================
7
8Benchmark jsmin implementations.
9
10:Copyright:
11
12 Copyright 2011 - 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.main [-c COUNT] [-p file] jsfile ...
32
33    -c COUNT  number of runs per jsfile and minifier. Defaults to 10.
34    -p file   File to write the benchmark results in (pickled)
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 sys as _sys
45import time as _time
46
47import_notes = []
48class jsmins(object):
49    from bench import jsmin as p_01_simple_port
50    if _sys.version_info >= (2, 4):
51        from bench import jsmin_2_0_9 as p_02_jsmin_2_0_9
52    else:
53        import_notes.append(
54            "jsmin_2_0_9 available for python 2.4 and later..."
55        )
56        print(import_notes[-1])
57
58    import rjsmin as p_05_rjsmin
59    try:
60        import _rjsmin as p_06__rjsmin
61    except ImportError:
62        import_notes.append("_rjsmin (C-Port) not available")
63        print(import_notes[-1])
64jsmins.p_05_rjsmin.jsmin = jsmins.p_05_rjsmin._make_jsmin(
65    python_only=True
66)
67print("Python Release: %s" % ".".join(map(str, _sys.version_info[:3])))
68print("")
69
70
71def slurp(filename):
72    """ Load a file """
73    fp = open(filename)
74    try:
75        return fp.read()
76    finally:
77        fp.close()
78
79
80def print_(*value, **kwargs):
81    """ Print stuff """
82    (kwargs.get('file') or _sys.stdout).write(
83        ''.join(value) + kwargs.get('end', '\n')
84    )
85
86
87def bench(filenames, count):
88    """
89    Benchmark the minifiers with given javascript samples
90
91    :Parameters:
92      `filenames` : sequence
93        List of filenames
94
95      `count` : ``int``
96        Number of runs per js file and minifier
97
98    :Exceptions:
99      - `RuntimeError` : empty filenames sequence
100    """
101    if not filenames:
102        raise RuntimeError("Missing files to benchmark")
103    try:
104        xrange
105    except NameError:
106        xrange = range
107    try:
108        cmp
109    except NameError:
110        cmp = lambda a, b: (a > b) - (a < b)
111
112    ports = [item for item in dir(jsmins) if item.startswith('p_')]
113    ports.sort()
114    space = max(map(len, ports)) - 4
115    ports = [(item[5:], getattr(jsmins, item).jsmin) for item in ports]
116    flush = _sys.stdout.flush
117
118    struct = []
119    inputs = [(filename, slurp(filename)) for filename in filenames]
120    for filename, script in inputs:
121        print_("Benchmarking %r..." % filename, end=" ")
122        flush()
123        outputs = []
124        for _, jsmin in ports:
125            try:
126                outputs.append(jsmin(script))
127            except (SystemExit, KeyboardInterrupt):
128                raise
129            except:
130                outputs.append(None)
131        struct.append(dict(
132            filename=filename,
133            sizes=[
134                (item is not None and len(item) or None) for item in outputs
135            ],
136            size=len(script),
137            messages=[],
138            times=[],
139        ))
140        print_("(%.1f KiB)" % (struct[-1]['size'] / 1024.0,))
141        flush()
142        times = []
143        for idx, (name, jsmin) in enumerate(ports):
144            if outputs[idx] is None:
145                print_("  FAILED %s" % (name,))
146                struct[-1]['times'].append((name, None))
147            else:
148                print_("  Timing %s%s... (%5.1f KiB %s)" % (
149                    name,
150                    " " * (space - len(name)),
151                    len(outputs[idx]) / 1024.0,
152                    idx == 0 and '*' or ['=', '>', '<'][
153                        cmp(len(outputs[idx]), len(outputs[0]))
154                    ],
155                ), end=" ")
156                flush()
157
158                xcount = count
159                while True:
160                    counted = [None for _ in xrange(xcount)]
161                    start = _time.time()
162                    for _ in counted:
163                        jsmin(script)
164                    end = _time.time()
165                    result = (end - start) * 1000
166                    if result < 10: # avoid measuring within the error range
167                        xcount *= 10
168                        continue
169                    times.append(result / xcount)
170                    break
171
172                print_("%8.2f ms" % times[-1], end=" ")
173                flush()
174                if len(times) <= 1:
175                    print_()
176                else:
177                    print_("(factor: %s)" % (', '.join([
178                        '%.2f' % (timed / times[-1]) for timed in times[:-1]
179                    ])))
180                struct[-1]['times'].append((name, times[-1]))
181
182            flush()
183        print_()
184
185    return struct
186
187
188def main(argv=None):
189    """ Main """
190    import getopt as _getopt
191    import os as _os
192    import pickle as _pickle
193
194    if argv is None:
195        argv = _sys.argv[1:]
196    try:
197        opts, args = _getopt.getopt(argv, "hc:p:", ["help"])
198    except getopt.GetoptError:
199        e = _sys.exc_info()[0](_sys.exc_info()[1])
200        print >> _sys.stderr, "%s\nTry %s -mbench.main --help" % (
201            e,
202            _os.path.basename(_sys.executable),
203        )
204        _sys.exit(2)
205
206    count, pickle = 10, None
207    for key, value in opts:
208        if key in ("-h", "--help"):
209            print >> _sys.stderr, (
210                "%s -mbench.main [-c count] [-p file] cssfile ..." % (
211                    _os.path.basename(_sys.executable),
212                )
213            )
214            _sys.exit(0)
215        elif key == '-c':
216            count = int(value)
217        elif key == '-p':
218            pickle = str(value)
219
220    struct = bench(args, count)
221    if pickle:
222        fp = open(pickle, 'wb')
223        try:
224            fp.write(_pickle.dumps((
225                ".".join(map(str, _sys.version_info[:3])),
226                import_notes,
227                struct,
228            ), 0))
229        finally:
230            fp.close()
231
232
233if __name__ == '__main__':
234    main()
235