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