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