blob: 714c0e427f9c872bbf22ad082530f87c2fdb98f6 [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 Cannond382bfc2012-07-20 14:54:53 -04007from test.test_importlib import util
8from test.test_importlib.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 Cannon810c64d2012-05-11 11:12:00 -040012import importlib.machinery
Brett Cannon190f33c2012-01-30 19:12:29 -050013import json
Brett Cannon3b0a19e2010-07-16 19:04:29 +000014import os
15import py_compile
Brett Cannon6ae7a7d2009-03-30 15:53:01 +000016import sys
Brett Cannon466e6a92012-02-07 09:19:12 -050017import tabnanny
Brett Cannon6ae7a7d2009-03-30 15:53:01 +000018import timeit
19
20
Brett Cannon23cf5742009-09-03 20:45:21 +000021def bench(name, cleanup=lambda: None, *, seconds=1, repeat=3):
22 """Bench the given statement as many times as necessary until total
23 executions take one second."""
24 stmt = "__import__({!r})".format(name)
25 timer = timeit.Timer(stmt)
26 for x in range(repeat):
27 total_time = 0
28 count = 0
29 while total_time < seconds:
30 try:
31 total_time += timer.timeit(1)
32 finally:
33 cleanup()
34 count += 1
35 else:
36 # One execution too far
37 if total_time > seconds:
38 count -= 1
Brett Cannon7b9bcb82010-07-15 06:24:04 +000039 yield count // seconds
Brett Cannon23cf5742009-09-03 20:45:21 +000040
Brett Cannon3b0a19e2010-07-16 19:04:29 +000041def from_cache(seconds, repeat):
Brett Cannon23cf5742009-09-03 20:45:21 +000042 """sys.modules"""
Brett Cannon6ae7a7d2009-03-30 15:53:01 +000043 name = '<benchmark import>'
Brett Cannon23cf5742009-09-03 20:45:21 +000044 module = imp.new_module(name)
45 module.__file__ = '<test>'
46 module.__package__ = ''
Brett Cannon6ae7a7d2009-03-30 15:53:01 +000047 with util.uncache(name):
Brett Cannon6ae7a7d2009-03-30 15:53:01 +000048 sys.modules[name] = module
Brett Cannon3b0a19e2010-07-16 19:04:29 +000049 for result in bench(name, repeat=repeat, seconds=seconds):
Brett Cannon23cf5742009-09-03 20:45:21 +000050 yield result
Brett Cannon6ae7a7d2009-03-30 15:53:01 +000051
52
Brett Cannon3b0a19e2010-07-16 19:04:29 +000053def builtin_mod(seconds, repeat):
Brett Cannon23cf5742009-09-03 20:45:21 +000054 """Built-in module"""
55 name = 'errno'
56 if name in sys.modules:
57 del sys.modules[name]
Brett Cannon7b9bcb82010-07-15 06:24:04 +000058 # Relying on built-in importer being implicit.
Brett Cannon3b0a19e2010-07-16 19:04:29 +000059 for result in bench(name, lambda: sys.modules.pop(name), repeat=repeat,
60 seconds=seconds):
Brett Cannon23cf5742009-09-03 20:45:21 +000061 yield result
Brett Cannon6ae7a7d2009-03-30 15:53:01 +000062
63
Brett Cannon3b0a19e2010-07-16 19:04:29 +000064def source_wo_bytecode(seconds, repeat):
Brett Cannon466e6a92012-02-07 09:19:12 -050065 """Source w/o bytecode: small"""
Brett Cannon3b0a19e2010-07-16 19:04:29 +000066 sys.dont_write_bytecode = True
67 try:
68 name = '__importlib_test_benchmark__'
69 # Clears out sys.modules and puts an entry at the front of sys.path.
70 with source_util.create_modules(name) as mapping:
71 assert not os.path.exists(imp.cache_from_source(mapping[name]))
Brett Cannon810c64d2012-05-11 11:12:00 -040072 sys.meta_path.append(importlib.machinery.PathFinder)
73 loader = (importlib.machinery.SourceFileLoader,
Brett Cannoncb66eb02012-05-11 12:58:42 -040074 importlib.machinery.SOURCE_SUFFIXES, True)
Brett Cannon810c64d2012-05-11 11:12:00 -040075 sys.path_hooks.append(importlib.machinery.FileFinder.path_hook(loader))
Brett Cannon3b0a19e2010-07-16 19:04:29 +000076 for result in bench(name, lambda: sys.modules.pop(name), repeat=repeat,
77 seconds=seconds):
78 yield result
79 finally:
80 sys.dont_write_bytecode = False
81
82
Brett Cannon466e6a92012-02-07 09:19:12 -050083def _wo_bytecode(module):
84 name = module.__name__
85 def benchmark_wo_bytecode(seconds, repeat):
86 """Source w/o bytecode: {}"""
87 bytecode_path = imp.cache_from_source(module.__file__)
88 if os.path.exists(bytecode_path):
89 os.unlink(bytecode_path)
90 sys.dont_write_bytecode = True
91 try:
92 for result in bench(name, lambda: sys.modules.pop(name),
93 repeat=repeat, seconds=seconds):
94 yield result
95 finally:
96 sys.dont_write_bytecode = False
97
98 benchmark_wo_bytecode.__doc__ = benchmark_wo_bytecode.__doc__.format(name)
99 return benchmark_wo_bytecode
100
101tabnanny_wo_bytecode = _wo_bytecode(tabnanny)
102decimal_wo_bytecode = _wo_bytecode(decimal)
Brett Cannon5db0c942010-07-22 07:40:56 +0000103
104
Brett Cannon3b0a19e2010-07-16 19:04:29 +0000105def source_writing_bytecode(seconds, repeat):
Brett Cannon466e6a92012-02-07 09:19:12 -0500106 """Source writing bytecode: small"""
Brett Cannon3b0a19e2010-07-16 19:04:29 +0000107 assert not sys.dont_write_bytecode
108 name = '__importlib_test_benchmark__'
109 with source_util.create_modules(name) as mapping:
Brett Cannon810c64d2012-05-11 11:12:00 -0400110 sys.meta_path.append(importlib.machinery.PathFinder)
111 loader = (importlib.machinery.SourceFileLoader,
Brett Cannoncb66eb02012-05-11 12:58:42 -0400112 importlib.machinery.SOURCE_SUFFIXES, True)
Brett Cannon810c64d2012-05-11 11:12:00 -0400113 sys.path_hooks.append(importlib.machinery.FileFinder.path_hook(loader))
Brett Cannon3b0a19e2010-07-16 19:04:29 +0000114 def cleanup():
115 sys.modules.pop(name)
116 os.unlink(imp.cache_from_source(mapping[name]))
117 for result in bench(name, cleanup, repeat=repeat, seconds=seconds):
118 assert not os.path.exists(imp.cache_from_source(mapping[name]))
119 yield result
120
121
Brett Cannon466e6a92012-02-07 09:19:12 -0500122def _writing_bytecode(module):
123 name = module.__name__
124 def writing_bytecode_benchmark(seconds, repeat):
125 """Source writing bytecode: {}"""
126 assert not sys.dont_write_bytecode
127 def cleanup():
128 sys.modules.pop(name)
129 os.unlink(imp.cache_from_source(module.__file__))
130 for result in bench(name, cleanup, repeat=repeat, seconds=seconds):
131 yield result
132
133 writing_bytecode_benchmark.__doc__ = (
134 writing_bytecode_benchmark.__doc__.format(name))
135 return writing_bytecode_benchmark
136
137tabnanny_writing_bytecode = _writing_bytecode(tabnanny)
138decimal_writing_bytecode = _writing_bytecode(decimal)
Brett Cannon5db0c942010-07-22 07:40:56 +0000139
140
Brett Cannon3b0a19e2010-07-16 19:04:29 +0000141def source_using_bytecode(seconds, repeat):
Brett Cannon466e6a92012-02-07 09:19:12 -0500142 """Source w/ bytecode: small"""
Brett Cannon3b0a19e2010-07-16 19:04:29 +0000143 name = '__importlib_test_benchmark__'
144 with source_util.create_modules(name) as mapping:
Brett Cannon810c64d2012-05-11 11:12:00 -0400145 sys.meta_path.append(importlib.machinery.PathFinder)
146 loader = (importlib.machinery.SourceFileLoader,
Brett Cannoncb66eb02012-05-11 12:58:42 -0400147 importlib.machinery.SOURCE_SUFFIXES, True)
Brett Cannon810c64d2012-05-11 11:12:00 -0400148 sys.path_hooks.append(importlib.machinery.FileFinder.path_hook(loader))
Brett Cannon3b0a19e2010-07-16 19:04:29 +0000149 py_compile.compile(mapping[name])
150 assert os.path.exists(imp.cache_from_source(mapping[name]))
151 for result in bench(name, lambda: sys.modules.pop(name), repeat=repeat,
152 seconds=seconds):
153 yield result
154
155
Brett Cannon466e6a92012-02-07 09:19:12 -0500156def _using_bytecode(module):
157 name = module.__name__
158 def using_bytecode_benchmark(seconds, repeat):
159 """Source w/ bytecode: {}"""
160 py_compile.compile(module.__file__)
161 for result in bench(name, lambda: sys.modules.pop(name), repeat=repeat,
162 seconds=seconds):
163 yield result
164
165 using_bytecode_benchmark.__doc__ = (
166 using_bytecode_benchmark.__doc__.format(name))
167 return using_bytecode_benchmark
168
169tabnanny_using_bytecode = _using_bytecode(tabnanny)
170decimal_using_bytecode = _using_bytecode(decimal)
Brett Cannon5db0c942010-07-22 07:40:56 +0000171
172
Brett Cannondfc32702012-02-23 19:34:35 -0500173def main(import_, options):
174 if options.source_file:
175 with options.source_file:
176 prev_results = json.load(options.source_file)
Brett Cannon190f33c2012-01-30 19:12:29 -0500177 else:
178 prev_results = {}
Brett Cannon23cf5742009-09-03 20:45:21 +0000179 __builtins__.__import__ = import_
Brett Cannon5db0c942010-07-22 07:40:56 +0000180 benchmarks = (from_cache, builtin_mod,
Brett Cannon5db0c942010-07-22 07:40:56 +0000181 source_writing_bytecode,
Brett Cannoncae10682012-02-07 09:40:33 -0500182 source_wo_bytecode, source_using_bytecode,
Brett Cannon466e6a92012-02-07 09:19:12 -0500183 tabnanny_writing_bytecode,
Brett Cannoncae10682012-02-07 09:40:33 -0500184 tabnanny_wo_bytecode, tabnanny_using_bytecode,
185 decimal_writing_bytecode,
186 decimal_wo_bytecode, decimal_using_bytecode,
187 )
Brett Cannondfc32702012-02-23 19:34:35 -0500188 if options.benchmark:
Brett Cannone3a9ae52012-01-30 19:27:51 -0500189 for b in benchmarks:
Brett Cannondfc32702012-02-23 19:34:35 -0500190 if b.__doc__ == options.benchmark:
Brett Cannone3a9ae52012-01-30 19:27:51 -0500191 benchmarks = [b]
192 break
193 else:
Brett Cannondfc32702012-02-23 19:34:35 -0500194 print('Unknown benchmark: {!r}'.format(options.benchmark,
195 file=sys.stderr))
Brett Cannone3a9ae52012-01-30 19:27:51 -0500196 sys.exit(1)
Brett Cannon5db0c942010-07-22 07:40:56 +0000197 seconds = 1
198 seconds_plural = 's' if seconds > 1 else ''
199 repeat = 3
Brett Cannon190f33c2012-01-30 19:12:29 -0500200 header = ('Measuring imports/second over {} second{}, best out of {}\n'
Brett Cannoncae10682012-02-07 09:40:33 -0500201 'Entire benchmark run should take about {} seconds\n'
202 'Using {!r} as __import__\n')
Brett Cannon190f33c2012-01-30 19:12:29 -0500203 print(header.format(seconds, seconds_plural, repeat,
Brett Cannoncae10682012-02-07 09:40:33 -0500204 len(benchmarks) * seconds * repeat, __import__))
Brett Cannon190f33c2012-01-30 19:12:29 -0500205 new_results = {}
Brett Cannon23cf5742009-09-03 20:45:21 +0000206 for benchmark in benchmarks:
207 print(benchmark.__doc__, "[", end=' ')
208 sys.stdout.flush()
209 results = []
Brett Cannon5db0c942010-07-22 07:40:56 +0000210 for result in benchmark(seconds=seconds, repeat=repeat):
Brett Cannon23cf5742009-09-03 20:45:21 +0000211 results.append(result)
212 print(result, end=' ')
213 sys.stdout.flush()
Brett Cannon3b0a19e2010-07-16 19:04:29 +0000214 assert not sys.dont_write_bytecode
Brett Cannoncbe1a4e2010-07-16 19:26:23 +0000215 print("]", "best is", format(max(results), ',d'))
Brett Cannon190f33c2012-01-30 19:12:29 -0500216 new_results[benchmark.__doc__] = results
Brett Cannondfc32702012-02-23 19:34:35 -0500217 if prev_results:
218 print('\n\nComparing new vs. old\n')
Brett Cannon190f33c2012-01-30 19:12:29 -0500219 for benchmark in benchmarks:
220 benchmark_name = benchmark.__doc__
Brett Cannondfc32702012-02-23 19:34:35 -0500221 old_result = max(prev_results[benchmark_name])
222 new_result = max(new_results[benchmark_name])
223 result = '{:,d} vs. {:,d} ({:%})'.format(new_result,
224 old_result,
225 new_result/old_result)
Brett Cannon190f33c2012-01-30 19:12:29 -0500226 print(benchmark_name, ':', result)
Brett Cannondfc32702012-02-23 19:34:35 -0500227 if options.dest_file:
228 with options.dest_file:
229 json.dump(new_results, options.dest_file, indent=2)
Brett Cannon6ae7a7d2009-03-30 15:53:01 +0000230
231
232if __name__ == '__main__':
Brett Cannon190f33c2012-01-30 19:12:29 -0500233 import argparse
Brett Cannon6ae7a7d2009-03-30 15:53:01 +0000234
Brett Cannon190f33c2012-01-30 19:12:29 -0500235 parser = argparse.ArgumentParser()
236 parser.add_argument('-b', '--builtin', dest='builtin', action='store_true',
Brett Cannon6ae7a7d2009-03-30 15:53:01 +0000237 default=False, help="use the built-in __import__")
Brett Cannondfc32702012-02-23 19:34:35 -0500238 parser.add_argument('-r', '--read', dest='source_file',
239 type=argparse.FileType('r'),
240 help='file to read benchmark data from to compare '
241 'against')
242 parser.add_argument('-w', '--write', dest='dest_file',
243 type=argparse.FileType('w'),
244 help='file to write benchmark data to')
Brett Cannone3a9ae52012-01-30 19:27:51 -0500245 parser.add_argument('--benchmark', dest='benchmark',
Brett Cannondfc32702012-02-23 19:34:35 -0500246 help='specific benchmark to run')
Brett Cannon190f33c2012-01-30 19:12:29 -0500247 options = parser.parse_args()
Brett Cannon6ae7a7d2009-03-30 15:53:01 +0000248 import_ = __import__
249 if not options.builtin:
250 import_ = importlib.__import__
251
Brett Cannondfc32702012-02-23 19:34:35 -0500252 main(import_, options)