blob: a8235cbe2ee8ea062991729781758bf0cb67869b [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
16import timeit
17
18
Brett Cannon23cf5742009-09-03 20:45:21 +000019def bench(name, cleanup=lambda: None, *, seconds=1, repeat=3):
20 """Bench the given statement as many times as necessary until total
21 executions take one second."""
22 stmt = "__import__({!r})".format(name)
23 timer = timeit.Timer(stmt)
24 for x in range(repeat):
25 total_time = 0
26 count = 0
27 while total_time < seconds:
28 try:
29 total_time += timer.timeit(1)
30 finally:
31 cleanup()
32 count += 1
33 else:
34 # One execution too far
35 if total_time > seconds:
36 count -= 1
Brett Cannon7b9bcb82010-07-15 06:24:04 +000037 yield count // seconds
Brett Cannon23cf5742009-09-03 20:45:21 +000038
Brett Cannon3b0a19e2010-07-16 19:04:29 +000039def from_cache(seconds, repeat):
Brett Cannon23cf5742009-09-03 20:45:21 +000040 """sys.modules"""
Brett Cannon6ae7a7d2009-03-30 15:53:01 +000041 name = '<benchmark import>'
Brett Cannon23cf5742009-09-03 20:45:21 +000042 module = imp.new_module(name)
43 module.__file__ = '<test>'
44 module.__package__ = ''
Brett Cannon6ae7a7d2009-03-30 15:53:01 +000045 with util.uncache(name):
Brett Cannon6ae7a7d2009-03-30 15:53:01 +000046 sys.modules[name] = module
Brett Cannon3b0a19e2010-07-16 19:04:29 +000047 for result in bench(name, repeat=repeat, seconds=seconds):
Brett Cannon23cf5742009-09-03 20:45:21 +000048 yield result
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.
Brett Cannon3b0a19e2010-07-16 19:04:29 +000057 for result in bench(name, lambda: sys.modules.pop(name), repeat=repeat,
58 seconds=seconds):
Brett Cannon23cf5742009-09-03 20:45:21 +000059 yield result
Brett Cannon6ae7a7d2009-03-30 15:53:01 +000060
61
Brett Cannon3b0a19e2010-07-16 19:04:29 +000062def source_wo_bytecode(seconds, repeat):
Brett Cannon5db0c942010-07-22 07:40:56 +000063 """Source w/o bytecode: simple"""
Brett Cannon3b0a19e2010-07-16 19:04:29 +000064 sys.dont_write_bytecode = True
65 try:
66 name = '__importlib_test_benchmark__'
67 # Clears out sys.modules and puts an entry at the front of sys.path.
68 with source_util.create_modules(name) as mapping:
69 assert not os.path.exists(imp.cache_from_source(mapping[name]))
70 for result in bench(name, lambda: sys.modules.pop(name), repeat=repeat,
71 seconds=seconds):
72 yield result
73 finally:
74 sys.dont_write_bytecode = False
75
76
Brett Cannon5db0c942010-07-22 07:40:56 +000077def decimal_wo_bytecode(seconds, repeat):
78 """Source w/o bytecode: decimal"""
79 name = 'decimal'
80 decimal_bytecode = imp.cache_from_source(decimal.__file__)
81 if os.path.exists(decimal_bytecode):
82 os.unlink(decimal_bytecode)
83 sys.dont_write_bytecode = True
84 try:
85 for result in bench(name, lambda: sys.modules.pop(name), repeat=repeat,
86 seconds=seconds):
87 yield result
88 finally:
89 sys.dont_write_bytecode = False
90
91
Brett Cannon3b0a19e2010-07-16 19:04:29 +000092def source_writing_bytecode(seconds, repeat):
Brett Cannon5db0c942010-07-22 07:40:56 +000093 """Source writing bytecode: simple"""
Brett Cannon3b0a19e2010-07-16 19:04:29 +000094 assert not sys.dont_write_bytecode
95 name = '__importlib_test_benchmark__'
96 with source_util.create_modules(name) as mapping:
97 def cleanup():
98 sys.modules.pop(name)
99 os.unlink(imp.cache_from_source(mapping[name]))
100 for result in bench(name, cleanup, repeat=repeat, seconds=seconds):
101 assert not os.path.exists(imp.cache_from_source(mapping[name]))
102 yield result
103
104
Brett Cannon5db0c942010-07-22 07:40:56 +0000105def decimal_writing_bytecode(seconds, repeat):
106 """Source writing bytecode: decimal"""
107 assert not sys.dont_write_bytecode
108 name = 'decimal'
109 def cleanup():
110 sys.modules.pop(name)
111 os.unlink(imp.cache_from_source(decimal.__file__))
112 for result in bench(name, cleanup, repeat=repeat, seconds=seconds):
113 yield result
114
115
Brett Cannon3b0a19e2010-07-16 19:04:29 +0000116def source_using_bytecode(seconds, repeat):
Brett Cannon5db0c942010-07-22 07:40:56 +0000117 """Bytecode w/ source: simple"""
Brett Cannon3b0a19e2010-07-16 19:04:29 +0000118 name = '__importlib_test_benchmark__'
119 with source_util.create_modules(name) as mapping:
120 py_compile.compile(mapping[name])
121 assert os.path.exists(imp.cache_from_source(mapping[name]))
122 for result in bench(name, lambda: sys.modules.pop(name), repeat=repeat,
123 seconds=seconds):
124 yield result
125
126
Brett Cannon5db0c942010-07-22 07:40:56 +0000127def decimal_using_bytecode(seconds, repeat):
128 """Bytecode w/ source: decimal"""
129 name = 'decimal'
130 py_compile.compile(decimal.__file__)
131 for result in bench(name, lambda: sys.modules.pop(name), repeat=repeat,
132 seconds=seconds):
133 yield result
134
135
Brett Cannone3a9ae52012-01-30 19:27:51 -0500136def main(import_, filename=None, benchmark=None):
Brett Cannon190f33c2012-01-30 19:12:29 -0500137 if filename and os.path.exists(filename):
138 with open(filename, 'r') as file:
139 prev_results = json.load(file)
140 else:
141 prev_results = {}
Brett Cannon23cf5742009-09-03 20:45:21 +0000142 __builtins__.__import__ = import_
Brett Cannon5db0c942010-07-22 07:40:56 +0000143 benchmarks = (from_cache, builtin_mod,
144 source_using_bytecode, source_wo_bytecode,
145 source_writing_bytecode,
146 decimal_using_bytecode, decimal_writing_bytecode,
147 decimal_wo_bytecode,)
Brett Cannone3a9ae52012-01-30 19:27:51 -0500148 if benchmark:
149 for b in benchmarks:
150 if b.__doc__ == benchmark:
151 benchmarks = [b]
152 break
153 else:
154 print('Unknown benchmark: {!r}'.format(benchmark, file=sys.stderr))
155 sys.exit(1)
Brett Cannon5db0c942010-07-22 07:40:56 +0000156 seconds = 1
157 seconds_plural = 's' if seconds > 1 else ''
158 repeat = 3
Brett Cannon190f33c2012-01-30 19:12:29 -0500159 header = ('Measuring imports/second over {} second{}, best out of {}\n'
160 'Entire benchmark run should take about {} seconds\n')
161 print(header.format(seconds, seconds_plural, repeat,
162 len(benchmarks) * seconds * repeat))
163 new_results = {}
Brett Cannon23cf5742009-09-03 20:45:21 +0000164 for benchmark in benchmarks:
165 print(benchmark.__doc__, "[", end=' ')
166 sys.stdout.flush()
167 results = []
Brett Cannon5db0c942010-07-22 07:40:56 +0000168 for result in benchmark(seconds=seconds, repeat=repeat):
Brett Cannon23cf5742009-09-03 20:45:21 +0000169 results.append(result)
170 print(result, end=' ')
171 sys.stdout.flush()
Brett Cannon3b0a19e2010-07-16 19:04:29 +0000172 assert not sys.dont_write_bytecode
Brett Cannoncbe1a4e2010-07-16 19:26:23 +0000173 print("]", "best is", format(max(results), ',d'))
Brett Cannon190f33c2012-01-30 19:12:29 -0500174 new_results[benchmark.__doc__] = results
175 prev_results[import_.__module__] = new_results
176 if 'importlib._bootstrap' in prev_results and 'builtins' in prev_results:
177 print('\n\nComparing importlib vs. __import__\n')
178 importlib_results = prev_results['importlib._bootstrap']
179 builtins_results = prev_results['builtins']
180 for benchmark in benchmarks:
181 benchmark_name = benchmark.__doc__
182 importlib_result = max(importlib_results[benchmark_name])
183 builtins_result = max(builtins_results[benchmark_name])
184 result = '{:,d} vs. {:,d} ({:%})'.format(importlib_result,
185 builtins_result,
186 importlib_result/builtins_result)
187 print(benchmark_name, ':', result)
188 if filename:
189 with open(filename, 'w') as file:
190 json.dump(prev_results, file, indent=2)
Brett Cannon6ae7a7d2009-03-30 15:53:01 +0000191
192
193if __name__ == '__main__':
Brett Cannon190f33c2012-01-30 19:12:29 -0500194 import argparse
Brett Cannon6ae7a7d2009-03-30 15:53:01 +0000195
Brett Cannon190f33c2012-01-30 19:12:29 -0500196 parser = argparse.ArgumentParser()
197 parser.add_argument('-b', '--builtin', dest='builtin', action='store_true',
Brett Cannon6ae7a7d2009-03-30 15:53:01 +0000198 default=False, help="use the built-in __import__")
Brett Cannon190f33c2012-01-30 19:12:29 -0500199 parser.add_argument('-f', '--file', dest='filename', default=None,
Brett Cannone3a9ae52012-01-30 19:27:51 -0500200 help='file to read/write results from/to'
201 '(incompatible w/ --benchmark)')
202 parser.add_argument('--benchmark', dest='benchmark',
203 help='specific benchmark to run '
204 '(incompatible w/ --file')
Brett Cannon190f33c2012-01-30 19:12:29 -0500205 options = parser.parse_args()
Brett Cannone3a9ae52012-01-30 19:27:51 -0500206 if options.filename and options.benchmark:
207 print('Cannot specify a benchmark *and* read/write results')
208 sys.exit(1)
Brett Cannon6ae7a7d2009-03-30 15:53:01 +0000209 import_ = __import__
210 if not options.builtin:
211 import_ = importlib.__import__
212
Brett Cannone3a9ae52012-01-30 19:27:51 -0500213 main(import_, options.filename, options.benchmark)