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