ยปCore Development>Code coverage>Tools/importbench/importbench.py

Python code coverage for Tools/importbench/importbench.py

#countcontent
1n/a"""Benchmark some basic import use-cases.
2n/a
3n/aThe assumption is made that this benchmark is run in a fresh interpreter and
4n/athus has no external changes made to import-related attributes in sys.
5n/a
6n/a"""
7n/afrom test.test_importlib import util
8n/aimport decimal
9n/aimport imp
10n/aimport importlib
11n/aimport importlib.machinery
12n/aimport json
13n/aimport os
14n/aimport py_compile
15n/aimport sys
16n/aimport tabnanny
17n/aimport timeit
18n/a
19n/a
20n/adef bench(name, cleanup=lambda: None, *, seconds=1, repeat=3):
21n/a """Bench the given statement as many times as necessary until total
22n/a executions take one second."""
23n/a stmt = "__import__({!r})".format(name)
24n/a timer = timeit.Timer(stmt)
25n/a for x in range(repeat):
26n/a total_time = 0
27n/a count = 0
28n/a while total_time < seconds:
29n/a try:
30n/a total_time += timer.timeit(1)
31n/a finally:
32n/a cleanup()
33n/a count += 1
34n/a else:
35n/a # One execution too far
36n/a if total_time > seconds:
37n/a count -= 1
38n/a yield count // seconds
39n/a
40n/adef from_cache(seconds, repeat):
41n/a """sys.modules"""
42n/a name = '<benchmark import>'
43n/a module = imp.new_module(name)
44n/a module.__file__ = '<test>'
45n/a module.__package__ = ''
46n/a with util.uncache(name):
47n/a sys.modules[name] = module
48n/a yield from bench(name, repeat=repeat, seconds=seconds)
49n/a
50n/a
51n/adef builtin_mod(seconds, repeat):
52n/a """Built-in module"""
53n/a name = 'errno'
54n/a if name in sys.modules:
55n/a del sys.modules[name]
56n/a # Relying on built-in importer being implicit.
57n/a yield from bench(name, lambda: sys.modules.pop(name), repeat=repeat,
58n/a seconds=seconds)
59n/a
60n/a
61n/adef source_wo_bytecode(seconds, repeat):
62n/a """Source w/o bytecode: small"""
63n/a sys.dont_write_bytecode = True
64n/a try:
65n/a name = '__importlib_test_benchmark__'
66n/a # Clears out sys.modules and puts an entry at the front of sys.path.
67n/a with util.create_modules(name) as mapping:
68n/a assert not os.path.exists(imp.cache_from_source(mapping[name]))
69n/a sys.meta_path.append(importlib.machinery.PathFinder)
70n/a loader = (importlib.machinery.SourceFileLoader,
71n/a importlib.machinery.SOURCE_SUFFIXES)
72n/a sys.path_hooks.append(importlib.machinery.FileFinder.path_hook(loader))
73n/a yield from bench(name, lambda: sys.modules.pop(name), repeat=repeat,
74n/a seconds=seconds)
75n/a finally:
76n/a sys.dont_write_bytecode = False
77n/a
78n/a
79n/adef _wo_bytecode(module):
80n/a name = module.__name__
81n/a def benchmark_wo_bytecode(seconds, repeat):
82n/a """Source w/o bytecode: {}"""
83n/a bytecode_path = imp.cache_from_source(module.__file__)
84n/a if os.path.exists(bytecode_path):
85n/a os.unlink(bytecode_path)
86n/a sys.dont_write_bytecode = True
87n/a try:
88n/a yield from bench(name, lambda: sys.modules.pop(name),
89n/a repeat=repeat, seconds=seconds)
90n/a finally:
91n/a sys.dont_write_bytecode = False
92n/a
93n/a benchmark_wo_bytecode.__doc__ = benchmark_wo_bytecode.__doc__.format(name)
94n/a return benchmark_wo_bytecode
95n/a
96n/atabnanny_wo_bytecode = _wo_bytecode(tabnanny)
97n/adecimal_wo_bytecode = _wo_bytecode(decimal)
98n/a
99n/a
100n/adef source_writing_bytecode(seconds, repeat):
101n/a """Source writing bytecode: small"""
102n/a assert not sys.dont_write_bytecode
103n/a name = '__importlib_test_benchmark__'
104n/a with util.create_modules(name) as mapping:
105n/a sys.meta_path.append(importlib.machinery.PathFinder)
106n/a loader = (importlib.machinery.SourceFileLoader,
107n/a importlib.machinery.SOURCE_SUFFIXES)
108n/a sys.path_hooks.append(importlib.machinery.FileFinder.path_hook(loader))
109n/a def cleanup():
110n/a sys.modules.pop(name)
111n/a os.unlink(imp.cache_from_source(mapping[name]))
112n/a for result in bench(name, cleanup, repeat=repeat, seconds=seconds):
113n/a assert not os.path.exists(imp.cache_from_source(mapping[name]))
114n/a yield result
115n/a
116n/a
117n/adef _writing_bytecode(module):
118n/a name = module.__name__
119n/a def writing_bytecode_benchmark(seconds, repeat):
120n/a """Source writing bytecode: {}"""
121n/a assert not sys.dont_write_bytecode
122n/a def cleanup():
123n/a sys.modules.pop(name)
124n/a os.unlink(imp.cache_from_source(module.__file__))
125n/a yield from bench(name, cleanup, repeat=repeat, seconds=seconds)
126n/a
127n/a writing_bytecode_benchmark.__doc__ = (
128n/a writing_bytecode_benchmark.__doc__.format(name))
129n/a return writing_bytecode_benchmark
130n/a
131n/atabnanny_writing_bytecode = _writing_bytecode(tabnanny)
132n/adecimal_writing_bytecode = _writing_bytecode(decimal)
133n/a
134n/a
135n/adef source_using_bytecode(seconds, repeat):
136n/a """Source w/ bytecode: small"""
137n/a name = '__importlib_test_benchmark__'
138n/a with util.create_modules(name) as mapping:
139n/a sys.meta_path.append(importlib.machinery.PathFinder)
140n/a loader = (importlib.machinery.SourceFileLoader,
141n/a importlib.machinery.SOURCE_SUFFIXES)
142n/a sys.path_hooks.append(importlib.machinery.FileFinder.path_hook(loader))
143n/a py_compile.compile(mapping[name])
144n/a assert os.path.exists(imp.cache_from_source(mapping[name]))
145n/a yield from bench(name, lambda: sys.modules.pop(name), repeat=repeat,
146n/a seconds=seconds)
147n/a
148n/a
149n/adef _using_bytecode(module):
150n/a name = module.__name__
151n/a def using_bytecode_benchmark(seconds, repeat):
152n/a """Source w/ bytecode: {}"""
153n/a py_compile.compile(module.__file__)
154n/a yield from bench(name, lambda: sys.modules.pop(name), repeat=repeat,
155n/a seconds=seconds)
156n/a
157n/a using_bytecode_benchmark.__doc__ = (
158n/a using_bytecode_benchmark.__doc__.format(name))
159n/a return using_bytecode_benchmark
160n/a
161n/atabnanny_using_bytecode = _using_bytecode(tabnanny)
162n/adecimal_using_bytecode = _using_bytecode(decimal)
163n/a
164n/a
165n/adef main(import_, options):
166n/a if options.source_file:
167n/a with options.source_file:
168n/a prev_results = json.load(options.source_file)
169n/a else:
170n/a prev_results = {}
171n/a __builtins__.__import__ = import_
172n/a benchmarks = (from_cache, builtin_mod,
173n/a source_writing_bytecode,
174n/a source_wo_bytecode, source_using_bytecode,
175n/a tabnanny_writing_bytecode,
176n/a tabnanny_wo_bytecode, tabnanny_using_bytecode,
177n/a decimal_writing_bytecode,
178n/a decimal_wo_bytecode, decimal_using_bytecode,
179n/a )
180n/a if options.benchmark:
181n/a for b in benchmarks:
182n/a if b.__doc__ == options.benchmark:
183n/a benchmarks = [b]
184n/a break
185n/a else:
186n/a print('Unknown benchmark: {!r}'.format(options.benchmark,
187n/a file=sys.stderr))
188n/a sys.exit(1)
189n/a seconds = 1
190n/a seconds_plural = 's' if seconds > 1 else ''
191n/a repeat = 3
192n/a header = ('Measuring imports/second over {} second{}, best out of {}\n'
193n/a 'Entire benchmark run should take about {} seconds\n'
194n/a 'Using {!r} as __import__\n')
195n/a print(header.format(seconds, seconds_plural, repeat,
196n/a len(benchmarks) * seconds * repeat, __import__))
197n/a new_results = {}
198n/a for benchmark in benchmarks:
199n/a print(benchmark.__doc__, "[", end=' ')
200n/a sys.stdout.flush()
201n/a results = []
202n/a for result in benchmark(seconds=seconds, repeat=repeat):
203n/a results.append(result)
204n/a print(result, end=' ')
205n/a sys.stdout.flush()
206n/a assert not sys.dont_write_bytecode
207n/a print("]", "best is", format(max(results), ',d'))
208n/a new_results[benchmark.__doc__] = results
209n/a if prev_results:
210n/a print('\n\nComparing new vs. old\n')
211n/a for benchmark in benchmarks:
212n/a benchmark_name = benchmark.__doc__
213n/a old_result = max(prev_results[benchmark_name])
214n/a new_result = max(new_results[benchmark_name])
215n/a result = '{:,d} vs. {:,d} ({:%})'.format(new_result,
216n/a old_result,
217n/a new_result/old_result)
218n/a print(benchmark_name, ':', result)
219n/a if options.dest_file:
220n/a with options.dest_file:
221n/a json.dump(new_results, options.dest_file, indent=2)
222n/a
223n/a
224n/aif __name__ == '__main__':
225n/a import argparse
226n/a
227n/a parser = argparse.ArgumentParser()
228n/a parser.add_argument('-b', '--builtin', dest='builtin', action='store_true',
229n/a default=False, help="use the built-in __import__")
230n/a parser.add_argument('-r', '--read', dest='source_file',
231n/a type=argparse.FileType('r'),
232n/a help='file to read benchmark data from to compare '
233n/a 'against')
234n/a parser.add_argument('-w', '--write', dest='dest_file',
235n/a type=argparse.FileType('w'),
236n/a help='file to write benchmark data to')
237n/a parser.add_argument('--benchmark', dest='benchmark',
238n/a help='specific benchmark to run')
239n/a options = parser.parse_args()
240n/a import_ = __import__
241n/a if not options.builtin:
242n/a import_ = importlib.__import__
243n/a
244n/a main(import_, options)