"""Benchmark some basic import use-cases. The assumption is made that this benchmark is run in a fresh interpreter and thus has no external changes made to import-related attributes in sys. """ from . import util from .source import util as source_util import imp import importlib import os import py_compile import sys import timeit def bench(name, cleanup=lambda: None, *, seconds=1, repeat=3): """Bench the given statement as many times as necessary until total executions take one second.""" stmt = "__import__({!r})".format(name) timer = timeit.Timer(stmt) for x in range(repeat): total_time = 0 count = 0 while total_time < seconds: try: total_time += timer.timeit(1) finally: cleanup() count += 1 else: # One execution too far if total_time > seconds: count -= 1 yield count // seconds def from_cache(seconds, repeat): """sys.modules""" name = '' module = imp.new_module(name) module.__file__ = '' module.__package__ = '' with util.uncache(name): sys.modules[name] = module for result in bench(name, repeat=repeat, seconds=seconds): yield result def builtin_mod(seconds, repeat): """Built-in module""" name = 'errno' if name in sys.modules: del sys.modules[name] # Relying on built-in importer being implicit. for result in bench(name, lambda: sys.modules.pop(name), repeat=repeat, seconds=seconds): yield result def source_wo_bytecode(seconds, repeat): """Source w/o bytecode""" sys.dont_write_bytecode = True try: name = '__importlib_test_benchmark__' # Clears out sys.modules and puts an entry at the front of sys.path. with source_util.create_modules(name) as mapping: assert not os.path.exists(imp.cache_from_source(mapping[name])) for result in bench(name, lambda: sys.modules.pop(name), repeat=repeat, seconds=seconds): yield result finally: sys.dont_write_bytecode = False def source_writing_bytecode(seconds, repeat): """Source writing bytecode""" assert not sys.dont_write_bytecode name = '__importlib_test_benchmark__' with source_util.create_modules(name) as mapping: def cleanup(): sys.modules.pop(name) os.unlink(imp.cache_from_source(mapping[name])) for result in bench(name, cleanup, repeat=repeat, seconds=seconds): assert not os.path.exists(imp.cache_from_source(mapping[name])) yield result def source_using_bytecode(seconds, repeat): """Bytecode w/ source""" name = '__importlib_test_benchmark__' with source_util.create_modules(name) as mapping: py_compile.compile(mapping[name]) assert os.path.exists(imp.cache_from_source(mapping[name])) for result in bench(name, lambda: sys.modules.pop(name), repeat=repeat, seconds=seconds): yield result def main(import_): __builtins__.__import__ = import_ benchmarks = (from_cache, builtin_mod, source_using_bytecode, source_wo_bytecode, source_writing_bytecode,) print("Measuring imports/second\n") for benchmark in benchmarks: print(benchmark.__doc__, "[", end=' ') sys.stdout.flush() results = [] for result in benchmark(seconds=1, repeat=3): results.append(result) print(result, end=' ') sys.stdout.flush() assert not sys.dont_write_bytecode print("]", "best is", format(max(results), ',d')) if __name__ == '__main__': import optparse parser = optparse.OptionParser() parser.add_option('-b', '--builtin', dest='builtin', action='store_true', default=False, help="use the built-in __import__") options, args = parser.parse_args() if args: raise RuntimeError("unrecognized args: {}".format(args)) import_ = __import__ if not options.builtin: import_ = importlib.__import__ main(import_)