blob: 025bc56c6e633f0ab22f9bcfc39e0026ea6af98d [file] [log] [blame]
Brett Cannon3b0a19e2010-07-16 19:04:29 +00001"""Benchmark some basic import use-cases.
2
3The assumption is made that this benchmark is run in a fresh interpreter and
4thus has no external changes made to import-related attributes in sys.
5
6"""
Brett Cannon6ae7a7d2009-03-30 15:53:01 +00007from . import util
8from .source import util as source_util
Brett Cannon5db0c942010-07-22 07:40:56 +00009import decimal
Brett Cannon6ae7a7d2009-03-30 15:53:01 +000010import imp
11import importlib
Brett Cannon190f33c2012-01-30 19:12:29 -050012import json
Brett Cannon3b0a19e2010-07-16 19:04:29 +000013import os
14import py_compile
Brett Cannon6ae7a7d2009-03-30 15:53:01 +000015import sys
Brett Cannon466e6a92012-02-07 09:19:12 -050016import tabnanny
Brett Cannon6ae7a7d2009-03-30 15:53:01 +000017import timeit
18
19
Brett Cannon23cf5742009-09-03 20:45:21 +000020def bench(name, cleanup=lambda: None, *, seconds=1, repeat=3):
21 """Bench the given statement as many times as necessary until total
22 executions take one second."""
23 stmt = "__import__({!r})".format(name)
24 timer = timeit.Timer(stmt)
25 for x in range(repeat):
26 total_time = 0
27 count = 0
28 while total_time < seconds:
29 try:
30 total_time += timer.timeit(1)
31 finally:
32 cleanup()
33 count += 1
34 else:
35 # One execution too far
36 if total_time > seconds:
37 count -= 1
Brett Cannon7b9bcb82010-07-15 06:24:04 +000038 yield count // seconds
Brett Cannon23cf5742009-09-03 20:45:21 +000039
Brett Cannon3b0a19e2010-07-16 19:04:29 +000040def from_cache(seconds, repeat):
Brett Cannon23cf5742009-09-03 20:45:21 +000041 """sys.modules"""
Brett Cannon6ae7a7d2009-03-30 15:53:01 +000042 name = '<benchmark import>'
Brett Cannon23cf5742009-09-03 20:45:21 +000043 module = imp.new_module(name)
44 module.__file__ = '<test>'
45 module.__package__ = ''
Brett Cannon6ae7a7d2009-03-30 15:53:01 +000046 with util.uncache(name):
Brett Cannon6ae7a7d2009-03-30 15:53:01 +000047 sys.modules[name] = module
Brett Cannon3b0a19e2010-07-16 19:04:29 +000048 for result in bench(name, repeat=repeat, seconds=seconds):
Brett Cannon23cf5742009-09-03 20:45:21 +000049 yield result
Brett Cannon6ae7a7d2009-03-30 15:53:01 +000050
51
Brett Cannon3b0a19e2010-07-16 19:04:29 +000052def builtin_mod(seconds, repeat):
Brett Cannon23cf5742009-09-03 20:45:21 +000053 """Built-in module"""
54 name = 'errno'
55 if name in sys.modules:
56 del sys.modules[name]
Brett Cannon7b9bcb82010-07-15 06:24:04 +000057 # Relying on built-in importer being implicit.
Brett Cannon3b0a19e2010-07-16 19:04:29 +000058 for result in bench(name, lambda: sys.modules.pop(name), repeat=repeat,
59 seconds=seconds):
Brett Cannon23cf5742009-09-03 20:45:21 +000060 yield result
Brett Cannon6ae7a7d2009-03-30 15:53:01 +000061
62
Brett Cannon3b0a19e2010-07-16 19:04:29 +000063def source_wo_bytecode(seconds, repeat):
Brett Cannon466e6a92012-02-07 09:19:12 -050064 """Source w/o bytecode: small"""
Brett Cannon3b0a19e2010-07-16 19:04:29 +000065 sys.dont_write_bytecode = True
66 try:
67 name = '__importlib_test_benchmark__'
68 # Clears out sys.modules and puts an entry at the front of sys.path.
69 with source_util.create_modules(name) as mapping:
70 assert not os.path.exists(imp.cache_from_source(mapping[name]))
71 for result in bench(name, lambda: sys.modules.pop(name), repeat=repeat,
72 seconds=seconds):
73 yield result
74 finally:
75 sys.dont_write_bytecode = False
76
77
Brett Cannon466e6a92012-02-07 09:19:12 -050078def _wo_bytecode(module):
79 name = module.__name__
80 def benchmark_wo_bytecode(seconds, repeat):
81 """Source w/o bytecode: {}"""
82 bytecode_path = imp.cache_from_source(module.__file__)
83 if os.path.exists(bytecode_path):
84 os.unlink(bytecode_path)
85 sys.dont_write_bytecode = True
86 try:
87 for result in bench(name, lambda: sys.modules.pop(name),
88 repeat=repeat, seconds=seconds):
89 yield result
90 finally:
91 sys.dont_write_bytecode = False
92
93 benchmark_wo_bytecode.__doc__ = benchmark_wo_bytecode.__doc__.format(name)
94 return benchmark_wo_bytecode
95
96tabnanny_wo_bytecode = _wo_bytecode(tabnanny)
97decimal_wo_bytecode = _wo_bytecode(decimal)
Brett Cannon5db0c942010-07-22 07:40:56 +000098
99
Brett Cannon3b0a19e2010-07-16 19:04:29 +0000100def source_writing_bytecode(seconds, repeat):
Brett Cannon466e6a92012-02-07 09:19:12 -0500101 """Source writing bytecode: small"""
Brett Cannon3b0a19e2010-07-16 19:04:29 +0000102 assert not sys.dont_write_bytecode
103 name = '__importlib_test_benchmark__'
104 with source_util.create_modules(name) as mapping:
105 def cleanup():
106 sys.modules.pop(name)
107 os.unlink(imp.cache_from_source(mapping[name]))
108 for result in bench(name, cleanup, repeat=repeat, seconds=seconds):
109 assert not os.path.exists(imp.cache_from_source(mapping[name]))
110 yield result
111
112
Brett Cannon466e6a92012-02-07 09:19:12 -0500113def _writing_bytecode(module):
114 name = module.__name__
115 def writing_bytecode_benchmark(seconds, repeat):
116 """Source writing bytecode: {}"""
117 assert not sys.dont_write_bytecode
118 def cleanup():
119 sys.modules.pop(name)
120 os.unlink(imp.cache_from_source(module.__file__))
121 for result in bench(name, cleanup, repeat=repeat, seconds=seconds):
122 yield result
123
124 writing_bytecode_benchmark.__doc__ = (
125 writing_bytecode_benchmark.__doc__.format(name))
126 return writing_bytecode_benchmark
127
128tabnanny_writing_bytecode = _writing_bytecode(tabnanny)
129decimal_writing_bytecode = _writing_bytecode(decimal)
Brett Cannon5db0c942010-07-22 07:40:56 +0000130
131
Brett Cannon3b0a19e2010-07-16 19:04:29 +0000132def source_using_bytecode(seconds, repeat):
Brett Cannon466e6a92012-02-07 09:19:12 -0500133 """Source w/ bytecode: small"""
Brett Cannon3b0a19e2010-07-16 19:04:29 +0000134 name = '__importlib_test_benchmark__'
135 with source_util.create_modules(name) as mapping:
136 py_compile.compile(mapping[name])
137 assert os.path.exists(imp.cache_from_source(mapping[name]))
138 for result in bench(name, lambda: sys.modules.pop(name), repeat=repeat,
139 seconds=seconds):
140 yield result
141
142
Brett Cannon466e6a92012-02-07 09:19:12 -0500143def _using_bytecode(module):
144 name = module.__name__
145 def using_bytecode_benchmark(seconds, repeat):
146 """Source w/ bytecode: {}"""
147 py_compile.compile(module.__file__)
148 for result in bench(name, lambda: sys.modules.pop(name), repeat=repeat,
149 seconds=seconds):
150 yield result
151
152 using_bytecode_benchmark.__doc__ = (
153 using_bytecode_benchmark.__doc__.format(name))
154 return using_bytecode_benchmark
155
156tabnanny_using_bytecode = _using_bytecode(tabnanny)
157decimal_using_bytecode = _using_bytecode(decimal)
Brett Cannon5db0c942010-07-22 07:40:56 +0000158
159
Brett Cannone3a9ae52012-01-30 19:27:51 -0500160def main(import_, filename=None, benchmark=None):
Brett Cannon190f33c2012-01-30 19:12:29 -0500161 if filename and os.path.exists(filename):
162 with open(filename, 'r') as file:
163 prev_results = json.load(file)
164 else:
165 prev_results = {}
Brett Cannon23cf5742009-09-03 20:45:21 +0000166 __builtins__.__import__ = import_
Brett Cannon5db0c942010-07-22 07:40:56 +0000167 benchmarks = (from_cache, builtin_mod,
168 source_using_bytecode, source_wo_bytecode,
169 source_writing_bytecode,
Brett Cannon466e6a92012-02-07 09:19:12 -0500170 tabnanny_using_bytecode, tabnanny_wo_bytecode,
171 tabnanny_writing_bytecode,
Brett Cannon5db0c942010-07-22 07:40:56 +0000172 decimal_using_bytecode, decimal_writing_bytecode,
173 decimal_wo_bytecode,)
Brett Cannone3a9ae52012-01-30 19:27:51 -0500174 if benchmark:
175 for b in benchmarks:
176 if b.__doc__ == benchmark:
177 benchmarks = [b]
178 break
179 else:
180 print('Unknown benchmark: {!r}'.format(benchmark, file=sys.stderr))
181 sys.exit(1)
Brett Cannon5db0c942010-07-22 07:40:56 +0000182 seconds = 1
183 seconds_plural = 's' if seconds > 1 else ''
184 repeat = 3
Brett Cannon190f33c2012-01-30 19:12:29 -0500185 header = ('Measuring imports/second over {} second{}, best out of {}\n'
186 'Entire benchmark run should take about {} seconds\n')
187 print(header.format(seconds, seconds_plural, repeat,
188 len(benchmarks) * seconds * repeat))
189 new_results = {}
Brett Cannon23cf5742009-09-03 20:45:21 +0000190 for benchmark in benchmarks:
191 print(benchmark.__doc__, "[", end=' ')
192 sys.stdout.flush()
193 results = []
Brett Cannon5db0c942010-07-22 07:40:56 +0000194 for result in benchmark(seconds=seconds, repeat=repeat):
Brett Cannon23cf5742009-09-03 20:45:21 +0000195 results.append(result)
196 print(result, end=' ')
197 sys.stdout.flush()
Brett Cannon3b0a19e2010-07-16 19:04:29 +0000198 assert not sys.dont_write_bytecode
Brett Cannoncbe1a4e2010-07-16 19:26:23 +0000199 print("]", "best is", format(max(results), ',d'))
Brett Cannon190f33c2012-01-30 19:12:29 -0500200 new_results[benchmark.__doc__] = results
201 prev_results[import_.__module__] = new_results
202 if 'importlib._bootstrap' in prev_results and 'builtins' in prev_results:
203 print('\n\nComparing importlib vs. __import__\n')
204 importlib_results = prev_results['importlib._bootstrap']
205 builtins_results = prev_results['builtins']
206 for benchmark in benchmarks:
207 benchmark_name = benchmark.__doc__
208 importlib_result = max(importlib_results[benchmark_name])
209 builtins_result = max(builtins_results[benchmark_name])
210 result = '{:,d} vs. {:,d} ({:%})'.format(importlib_result,
211 builtins_result,
212 importlib_result/builtins_result)
213 print(benchmark_name, ':', result)
214 if filename:
215 with open(filename, 'w') as file:
216 json.dump(prev_results, file, indent=2)
Brett Cannon6ae7a7d2009-03-30 15:53:01 +0000217
218
219if __name__ == '__main__':
Brett Cannon190f33c2012-01-30 19:12:29 -0500220 import argparse
Brett Cannon6ae7a7d2009-03-30 15:53:01 +0000221
Brett Cannon190f33c2012-01-30 19:12:29 -0500222 parser = argparse.ArgumentParser()
223 parser.add_argument('-b', '--builtin', dest='builtin', action='store_true',
Brett Cannon6ae7a7d2009-03-30 15:53:01 +0000224 default=False, help="use the built-in __import__")
Brett Cannon190f33c2012-01-30 19:12:29 -0500225 parser.add_argument('-f', '--file', dest='filename', default=None,
Brett Cannone3a9ae52012-01-30 19:27:51 -0500226 help='file to read/write results from/to'
227 '(incompatible w/ --benchmark)')
228 parser.add_argument('--benchmark', dest='benchmark',
229 help='specific benchmark to run '
Brett Cannon5ea5b672012-01-31 17:02:10 -0500230 '(incompatible w/ --file)')
Brett Cannon190f33c2012-01-30 19:12:29 -0500231 options = parser.parse_args()
Brett Cannone3a9ae52012-01-30 19:27:51 -0500232 if options.filename and options.benchmark:
233 print('Cannot specify a benchmark *and* read/write results')
234 sys.exit(1)
Brett Cannon6ae7a7d2009-03-30 15:53:01 +0000235 import_ = __import__
236 if not options.builtin:
237 import_ = importlib.__import__
238
Brett Cannone3a9ae52012-01-30 19:27:51 -0500239 main(import_, options.filename, options.benchmark)