blob: e2ef75836ed45e5e96d2db6cdeb6c0255cde4770 [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
Brett Cannon5db0c942010-07-22 07:40:56 +00008import decimal
Brett Cannon6ae7a7d2009-03-30 15:53:01 +00009import imp
10import importlib
Brett Cannon810c64d2012-05-11 11:12:00 -040011import importlib.machinery
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
Philip Jenveyfd0d3e52012-10-01 15:34:31 -070048 yield from bench(name, repeat=repeat, seconds=seconds)
Brett Cannon6ae7a7d2009-03-30 15:53:01 +000049
50
Brett Cannon3b0a19e2010-07-16 19:04:29 +000051def builtin_mod(seconds, repeat):
Brett Cannon23cf5742009-09-03 20:45:21 +000052 """Built-in module"""
53 name = 'errno'
54 if name in sys.modules:
55 del sys.modules[name]
Brett Cannon7b9bcb82010-07-15 06:24:04 +000056 # Relying on built-in importer being implicit.
Philip Jenveyfd0d3e52012-10-01 15:34:31 -070057 yield from bench(name, lambda: sys.modules.pop(name), repeat=repeat,
58 seconds=seconds)
Brett Cannon6ae7a7d2009-03-30 15:53:01 +000059
60
Brett Cannon3b0a19e2010-07-16 19:04:29 +000061def source_wo_bytecode(seconds, repeat):
Brett Cannon466e6a92012-02-07 09:19:12 -050062 """Source w/o bytecode: small"""
Brett Cannon3b0a19e2010-07-16 19:04:29 +000063 sys.dont_write_bytecode = True
64 try:
65 name = '__importlib_test_benchmark__'
66 # Clears out sys.modules and puts an entry at the front of sys.path.
Serhiy Storchakaeb51faa2016-06-30 10:33:17 +030067 with util.create_modules(name) as mapping:
Brett Cannon3b0a19e2010-07-16 19:04:29 +000068 assert not os.path.exists(imp.cache_from_source(mapping[name]))
Brett Cannon810c64d2012-05-11 11:12:00 -040069 sys.meta_path.append(importlib.machinery.PathFinder)
70 loader = (importlib.machinery.SourceFileLoader,
Serhiy Storchakaeb51faa2016-06-30 10:33:17 +030071 importlib.machinery.SOURCE_SUFFIXES)
Brett Cannon810c64d2012-05-11 11:12:00 -040072 sys.path_hooks.append(importlib.machinery.FileFinder.path_hook(loader))
Philip Jenveyfd0d3e52012-10-01 15:34:31 -070073 yield from bench(name, lambda: sys.modules.pop(name), repeat=repeat,
74 seconds=seconds)
Brett Cannon3b0a19e2010-07-16 19:04:29 +000075 finally:
76 sys.dont_write_bytecode = False
77
78
Brett Cannon466e6a92012-02-07 09:19:12 -050079def _wo_bytecode(module):
80 name = module.__name__
81 def benchmark_wo_bytecode(seconds, repeat):
82 """Source w/o bytecode: {}"""
83 bytecode_path = imp.cache_from_source(module.__file__)
84 if os.path.exists(bytecode_path):
85 os.unlink(bytecode_path)
86 sys.dont_write_bytecode = True
87 try:
Philip Jenveyfd0d3e52012-10-01 15:34:31 -070088 yield from bench(name, lambda: sys.modules.pop(name),
89 repeat=repeat, seconds=seconds)
Brett Cannon466e6a92012-02-07 09:19:12 -050090 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__'
Serhiy Storchakaeb51faa2016-06-30 10:33:17 +0300104 with util.create_modules(name) as mapping:
Brett Cannon810c64d2012-05-11 11:12:00 -0400105 sys.meta_path.append(importlib.machinery.PathFinder)
106 loader = (importlib.machinery.SourceFileLoader,
Serhiy Storchakaeb51faa2016-06-30 10:33:17 +0300107 importlib.machinery.SOURCE_SUFFIXES)
Brett Cannon810c64d2012-05-11 11:12:00 -0400108 sys.path_hooks.append(importlib.machinery.FileFinder.path_hook(loader))
Brett Cannon3b0a19e2010-07-16 19:04:29 +0000109 def cleanup():
110 sys.modules.pop(name)
111 os.unlink(imp.cache_from_source(mapping[name]))
112 for result in bench(name, cleanup, repeat=repeat, seconds=seconds):
113 assert not os.path.exists(imp.cache_from_source(mapping[name]))
114 yield result
115
116
Brett Cannon466e6a92012-02-07 09:19:12 -0500117def _writing_bytecode(module):
118 name = module.__name__
119 def writing_bytecode_benchmark(seconds, repeat):
120 """Source writing bytecode: {}"""
121 assert not sys.dont_write_bytecode
122 def cleanup():
123 sys.modules.pop(name)
124 os.unlink(imp.cache_from_source(module.__file__))
Philip Jenveyfd0d3e52012-10-01 15:34:31 -0700125 yield from bench(name, cleanup, repeat=repeat, seconds=seconds)
Brett Cannon466e6a92012-02-07 09:19:12 -0500126
127 writing_bytecode_benchmark.__doc__ = (
128 writing_bytecode_benchmark.__doc__.format(name))
129 return writing_bytecode_benchmark
130
131tabnanny_writing_bytecode = _writing_bytecode(tabnanny)
132decimal_writing_bytecode = _writing_bytecode(decimal)
Brett Cannon5db0c942010-07-22 07:40:56 +0000133
134
Brett Cannon3b0a19e2010-07-16 19:04:29 +0000135def source_using_bytecode(seconds, repeat):
Brett Cannon466e6a92012-02-07 09:19:12 -0500136 """Source w/ bytecode: small"""
Brett Cannon3b0a19e2010-07-16 19:04:29 +0000137 name = '__importlib_test_benchmark__'
Serhiy Storchakaeb51faa2016-06-30 10:33:17 +0300138 with util.create_modules(name) as mapping:
Brett Cannon810c64d2012-05-11 11:12:00 -0400139 sys.meta_path.append(importlib.machinery.PathFinder)
140 loader = (importlib.machinery.SourceFileLoader,
Serhiy Storchakaeb51faa2016-06-30 10:33:17 +0300141 importlib.machinery.SOURCE_SUFFIXES)
Brett Cannon810c64d2012-05-11 11:12:00 -0400142 sys.path_hooks.append(importlib.machinery.FileFinder.path_hook(loader))
Brett Cannon3b0a19e2010-07-16 19:04:29 +0000143 py_compile.compile(mapping[name])
144 assert os.path.exists(imp.cache_from_source(mapping[name]))
Philip Jenveyfd0d3e52012-10-01 15:34:31 -0700145 yield from bench(name, lambda: sys.modules.pop(name), repeat=repeat,
146 seconds=seconds)
Brett Cannon3b0a19e2010-07-16 19:04:29 +0000147
148
Brett Cannon466e6a92012-02-07 09:19:12 -0500149def _using_bytecode(module):
150 name = module.__name__
151 def using_bytecode_benchmark(seconds, repeat):
152 """Source w/ bytecode: {}"""
153 py_compile.compile(module.__file__)
Philip Jenveyfd0d3e52012-10-01 15:34:31 -0700154 yield from bench(name, lambda: sys.modules.pop(name), repeat=repeat,
155 seconds=seconds)
Brett Cannon466e6a92012-02-07 09:19:12 -0500156
157 using_bytecode_benchmark.__doc__ = (
158 using_bytecode_benchmark.__doc__.format(name))
159 return using_bytecode_benchmark
160
161tabnanny_using_bytecode = _using_bytecode(tabnanny)
162decimal_using_bytecode = _using_bytecode(decimal)
Brett Cannon5db0c942010-07-22 07:40:56 +0000163
164
Brett Cannondfc32702012-02-23 19:34:35 -0500165def main(import_, options):
166 if options.source_file:
167 with options.source_file:
168 prev_results = json.load(options.source_file)
Brett Cannon190f33c2012-01-30 19:12:29 -0500169 else:
170 prev_results = {}
Brett Cannon23cf5742009-09-03 20:45:21 +0000171 __builtins__.__import__ = import_
Brett Cannon5db0c942010-07-22 07:40:56 +0000172 benchmarks = (from_cache, builtin_mod,
Brett Cannon5db0c942010-07-22 07:40:56 +0000173 source_writing_bytecode,
Brett Cannoncae10682012-02-07 09:40:33 -0500174 source_wo_bytecode, source_using_bytecode,
Brett Cannon466e6a92012-02-07 09:19:12 -0500175 tabnanny_writing_bytecode,
Brett Cannoncae10682012-02-07 09:40:33 -0500176 tabnanny_wo_bytecode, tabnanny_using_bytecode,
177 decimal_writing_bytecode,
178 decimal_wo_bytecode, decimal_using_bytecode,
179 )
Brett Cannondfc32702012-02-23 19:34:35 -0500180 if options.benchmark:
Brett Cannone3a9ae52012-01-30 19:27:51 -0500181 for b in benchmarks:
Brett Cannondfc32702012-02-23 19:34:35 -0500182 if b.__doc__ == options.benchmark:
Brett Cannone3a9ae52012-01-30 19:27:51 -0500183 benchmarks = [b]
184 break
185 else:
Brett Cannondfc32702012-02-23 19:34:35 -0500186 print('Unknown benchmark: {!r}'.format(options.benchmark,
187 file=sys.stderr))
Brett Cannone3a9ae52012-01-30 19:27:51 -0500188 sys.exit(1)
Brett Cannon5db0c942010-07-22 07:40:56 +0000189 seconds = 1
190 seconds_plural = 's' if seconds > 1 else ''
191 repeat = 3
Brett Cannon190f33c2012-01-30 19:12:29 -0500192 header = ('Measuring imports/second over {} second{}, best out of {}\n'
Brett Cannoncae10682012-02-07 09:40:33 -0500193 'Entire benchmark run should take about {} seconds\n'
194 'Using {!r} as __import__\n')
Brett Cannon190f33c2012-01-30 19:12:29 -0500195 print(header.format(seconds, seconds_plural, repeat,
Brett Cannoncae10682012-02-07 09:40:33 -0500196 len(benchmarks) * seconds * repeat, __import__))
Brett Cannon190f33c2012-01-30 19:12:29 -0500197 new_results = {}
Brett Cannon23cf5742009-09-03 20:45:21 +0000198 for benchmark in benchmarks:
199 print(benchmark.__doc__, "[", end=' ')
200 sys.stdout.flush()
201 results = []
Brett Cannon5db0c942010-07-22 07:40:56 +0000202 for result in benchmark(seconds=seconds, repeat=repeat):
Brett Cannon23cf5742009-09-03 20:45:21 +0000203 results.append(result)
204 print(result, end=' ')
205 sys.stdout.flush()
Brett Cannon3b0a19e2010-07-16 19:04:29 +0000206 assert not sys.dont_write_bytecode
Brett Cannoncbe1a4e2010-07-16 19:26:23 +0000207 print("]", "best is", format(max(results), ',d'))
Brett Cannon190f33c2012-01-30 19:12:29 -0500208 new_results[benchmark.__doc__] = results
Brett Cannondfc32702012-02-23 19:34:35 -0500209 if prev_results:
210 print('\n\nComparing new vs. old\n')
Brett Cannon190f33c2012-01-30 19:12:29 -0500211 for benchmark in benchmarks:
212 benchmark_name = benchmark.__doc__
Brett Cannondfc32702012-02-23 19:34:35 -0500213 old_result = max(prev_results[benchmark_name])
214 new_result = max(new_results[benchmark_name])
215 result = '{:,d} vs. {:,d} ({:%})'.format(new_result,
216 old_result,
217 new_result/old_result)
Brett Cannon190f33c2012-01-30 19:12:29 -0500218 print(benchmark_name, ':', result)
Brett Cannondfc32702012-02-23 19:34:35 -0500219 if options.dest_file:
220 with options.dest_file:
221 json.dump(new_results, options.dest_file, indent=2)
Brett Cannon6ae7a7d2009-03-30 15:53:01 +0000222
223
224if __name__ == '__main__':
Brett Cannon190f33c2012-01-30 19:12:29 -0500225 import argparse
Brett Cannon6ae7a7d2009-03-30 15:53:01 +0000226
Brett Cannon190f33c2012-01-30 19:12:29 -0500227 parser = argparse.ArgumentParser()
228 parser.add_argument('-b', '--builtin', dest='builtin', action='store_true',
Brett Cannon6ae7a7d2009-03-30 15:53:01 +0000229 default=False, help="use the built-in __import__")
Brett Cannondfc32702012-02-23 19:34:35 -0500230 parser.add_argument('-r', '--read', dest='source_file',
231 type=argparse.FileType('r'),
232 help='file to read benchmark data from to compare '
233 'against')
234 parser.add_argument('-w', '--write', dest='dest_file',
235 type=argparse.FileType('w'),
236 help='file to write benchmark data to')
Brett Cannone3a9ae52012-01-30 19:27:51 -0500237 parser.add_argument('--benchmark', dest='benchmark',
Brett Cannondfc32702012-02-23 19:34:35 -0500238 help='specific benchmark to run')
Brett Cannon190f33c2012-01-30 19:12:29 -0500239 options = parser.parse_args()
Brett Cannon6ae7a7d2009-03-30 15:53:01 +0000240 import_ = __import__
241 if not options.builtin:
242 import_ = importlib.__import__
243
Brett Cannondfc32702012-02-23 19:34:35 -0500244 main(import_, options)