ยปCore Development>Code coverage>Lib/importlib/test/benchmark.py

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