annotate tests/check-perf-code.py @ 44118:f81c17ec303c

hgdemandimport: apply lazy module loading to sys.meta_path finders Python's `sys.meta_path` finders are the primary objects whose job it is to find a module at import time. When `import` is called, Python iterates objects in this list and calls `o.find_spec(...)` to find a `ModuleSpec` (or None if the module couldn't be found by that finder). If no meta path finder can find a module, import fails. One of the default meta path finders is `PathFinder`. Its job is to import modules from the filesystem and is probably the most important importer. This finder looks at `sys.path` and `sys.path_hooks` to do its job. The `ModuleSpec` returned by `MetaPathImporter.find_spec()` has a `loader` attribute, which defines the concrete module loader to use. `sys.path_hooks` is a hook point for teaching `PathFinder` to instantiate custom loader types. Previously, we injected a custom `sys.path_hook` that told `PathFinder` to wrap the default loaders with a loader that creates a module object that is lazy. This approach worked. But its main limitation was that it only applied to the `PathFinder` meta path importer. There are other meta path importers that are registered. And in the case of PyOxidizer loading modules from memory, `PathFinder` doesn't come into play since PyOxidizer's own meta path importer was handling all imports. This commit changes our approach to lazy module loading by proxying all meta path importers. Specifically, we overload the `find_spec()` method to swap in a wrapped loader on the `ModuleSpec` before it is returned. The end result of this is all meta path importers should be lazy. As much as I would have loved to utilize .__class__ manipulation to achieve this, some meta path importers are implemented in C/Rust in such a way that they cannot be monkeypatched. This is why we use __getattribute__ to define a proxy. Also, this change could theoretically open us up to regressions in meta path importers whose loader is creating module objects which can't be monkeypatched. But I'm not aware of any of these in the wild. So I think we'll be safe. According to hyperfine, this change yields a decent startup time win of 5-6ms: ``` Benchmark #1: ~/.pyenv/versions/3.6.10/bin/python ./hg version Time (mean ± σ): 86.8 ms ± 0.5 ms [User: 78.0 ms, System: 8.7 ms] Range (min … max): 86.0 ms … 89.1 ms 50 runs Time (mean ± σ): 81.1 ms ± 2.7 ms [User: 74.5 ms, System: 6.5 ms] Range (min … max): 77.8 ms … 90.5 ms 50 runs Benchmark #2: ~/.pyenv/versions/3.7.6/bin/python ./hg version Time (mean ± σ): 78.9 ms ± 0.6 ms [User: 70.2 ms, System: 8.7 ms] Range (min … max): 78.1 ms … 81.2 ms 50 runs Time (mean ± σ): 73.4 ms ± 0.6 ms [User: 65.3 ms, System: 8.0 ms] Range (min … max): 72.4 ms … 75.7 ms 50 runs Benchmark #3: ~/.pyenv/versions/3.8.1/bin/python ./hg version Time (mean ± σ): 78.1 ms ± 0.6 ms [User: 70.2 ms, System: 7.9 ms] Range (min … max): 77.4 ms … 80.9 ms 50 runs Time (mean ± σ): 72.1 ms ± 0.4 ms [User: 64.4 ms, System: 7.6 ms] Range (min … max): 71.4 ms … 74.1 ms 50 runs ``` Differential Revision: https://phab.mercurial-scm.org/D7954
author Gregory Szorc <gregory.szorc@gmail.com>
date Mon, 20 Jan 2020 23:51:25 -0800
parents 2372284d9457
children c102b704edb5
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
29570
cbd240188e4e tests: introduce check-perf-code.py to add extra checks on perf.py
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
diff changeset
1 #!/usr/bin/env python
cbd240188e4e tests: introduce check-perf-code.py to add extra checks on perf.py
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
diff changeset
2 #
cbd240188e4e tests: introduce check-perf-code.py to add extra checks on perf.py
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
diff changeset
3 # check-perf-code - (historical) portability checker for contrib/perf.py
cbd240188e4e tests: introduce check-perf-code.py to add extra checks on perf.py
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
diff changeset
4
cbd240188e4e tests: introduce check-perf-code.py to add extra checks on perf.py
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
diff changeset
5 from __future__ import absolute_import
cbd240188e4e tests: introduce check-perf-code.py to add extra checks on perf.py
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
diff changeset
6
cbd240188e4e tests: introduce check-perf-code.py to add extra checks on perf.py
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
diff changeset
7 import os
cbd240188e4e tests: introduce check-perf-code.py to add extra checks on perf.py
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
diff changeset
8 import sys
cbd240188e4e tests: introduce check-perf-code.py to add extra checks on perf.py
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
diff changeset
9
cbd240188e4e tests: introduce check-perf-code.py to add extra checks on perf.py
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
diff changeset
10 # write static check patterns here
cbd240188e4e tests: introduce check-perf-code.py to add extra checks on perf.py
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
diff changeset
11 perfpypats = [
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42138
diff changeset
12 [
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42138
diff changeset
13 (
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42138
diff changeset
14 r'(branchmap|repoview|repoviewutil)\.subsettable',
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42138
diff changeset
15 "use getbranchmapsubsettable() for early Mercurial",
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42138
diff changeset
16 ),
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42138
diff changeset
17 (
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42138
diff changeset
18 r'\.(vfs|svfs|opener|sopener)',
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42138
diff changeset
19 "use getvfs()/getsvfs() for early Mercurial",
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42138
diff changeset
20 ),
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42138
diff changeset
21 (
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42138
diff changeset
22 r'ui\.configint',
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42138
diff changeset
23 "use getint() instead of ui.configint() for early Mercurial",
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42138
diff changeset
24 ),
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42138
diff changeset
25 ],
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42138
diff changeset
26 # warnings
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42138
diff changeset
27 [],
29570
cbd240188e4e tests: introduce check-perf-code.py to add extra checks on perf.py
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
diff changeset
28 ]
cbd240188e4e tests: introduce check-perf-code.py to add extra checks on perf.py
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
diff changeset
29
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42138
diff changeset
30
29571
d1a7d9c279bb tests: check importing modules in perf.py for historical portability
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 29570
diff changeset
31 def modulewhitelist(names):
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42138
diff changeset
32 replacement = [
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42138
diff changeset
33 ('.py', ''),
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42138
diff changeset
34 ('.c', ''), # trim suffix
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42138
diff changeset
35 ('mercurial%s' % '/', ''), # trim "mercurial/" path
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42138
diff changeset
36 ]
32291
bd872f64a8ba cleanup: use set literals
Martin von Zweigbergk <martinvonz@google.com>
parents: 30149
diff changeset
37 ignored = {'__init__'}
29571
d1a7d9c279bb tests: check importing modules in perf.py for historical portability
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 29570
diff changeset
38 modules = {}
d1a7d9c279bb tests: check importing modules in perf.py for historical portability
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 29570
diff changeset
39
d1a7d9c279bb tests: check importing modules in perf.py for historical portability
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 29570
diff changeset
40 # convert from file name to module name, and count # of appearances
d1a7d9c279bb tests: check importing modules in perf.py for historical portability
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 29570
diff changeset
41 for name in names:
d1a7d9c279bb tests: check importing modules in perf.py for historical portability
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 29570
diff changeset
42 name = name.strip()
d1a7d9c279bb tests: check importing modules in perf.py for historical portability
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 29570
diff changeset
43 for old, new in replacement:
d1a7d9c279bb tests: check importing modules in perf.py for historical portability
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 29570
diff changeset
44 name = name.replace(old, new)
d1a7d9c279bb tests: check importing modules in perf.py for historical portability
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 29570
diff changeset
45 if name not in ignored:
d1a7d9c279bb tests: check importing modules in perf.py for historical portability
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 29570
diff changeset
46 modules[name] = modules.get(name, 0) + 1
d1a7d9c279bb tests: check importing modules in perf.py for historical portability
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 29570
diff changeset
47
d1a7d9c279bb tests: check importing modules in perf.py for historical portability
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 29570
diff changeset
48 # list up module names, which appear multiple times
d1a7d9c279bb tests: check importing modules in perf.py for historical portability
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 29570
diff changeset
49 whitelist = []
d1a7d9c279bb tests: check importing modules in perf.py for historical portability
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 29570
diff changeset
50 for name, count in modules.items():
d1a7d9c279bb tests: check importing modules in perf.py for historical portability
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 29570
diff changeset
51 if count > 1:
d1a7d9c279bb tests: check importing modules in perf.py for historical portability
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 29570
diff changeset
52 whitelist.append(name)
d1a7d9c279bb tests: check importing modules in perf.py for historical portability
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 29570
diff changeset
53
d1a7d9c279bb tests: check importing modules in perf.py for historical portability
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 29570
diff changeset
54 return whitelist
d1a7d9c279bb tests: check importing modules in perf.py for historical portability
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 29570
diff changeset
55
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42138
diff changeset
56
29570
cbd240188e4e tests: introduce check-perf-code.py to add extra checks on perf.py
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
diff changeset
57 if __name__ == "__main__":
29571
d1a7d9c279bb tests: check importing modules in perf.py for historical portability
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 29570
diff changeset
58 # in this case, it is assumed that result of "hg files" at
d1a7d9c279bb tests: check importing modules in perf.py for historical portability
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 29570
diff changeset
59 # multiple revisions is given via stdin
d1a7d9c279bb tests: check importing modules in perf.py for historical portability
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 29570
diff changeset
60 whitelist = modulewhitelist(sys.stdin)
d1a7d9c279bb tests: check importing modules in perf.py for historical portability
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 29570
diff changeset
61 assert whitelist, "module whitelist is empty"
d1a7d9c279bb tests: check importing modules in perf.py for historical portability
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 29570
diff changeset
62
d1a7d9c279bb tests: check importing modules in perf.py for historical portability
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 29570
diff changeset
63 # build up module whitelist check from file names given at runtime
d1a7d9c279bb tests: check importing modules in perf.py for historical portability
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 29570
diff changeset
64 perfpypats[0].append(
d1a7d9c279bb tests: check importing modules in perf.py for historical portability
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 29570
diff changeset
65 # this matching pattern assumes importing modules from
d1a7d9c279bb tests: check importing modules in perf.py for historical portability
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 29570
diff changeset
66 # "mercurial" package in the current style below, for simplicity
d1a7d9c279bb tests: check importing modules in perf.py for historical portability
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 29570
diff changeset
67 #
d1a7d9c279bb tests: check importing modules in perf.py for historical portability
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 29570
diff changeset
68 # from mercurial import (
d1a7d9c279bb tests: check importing modules in perf.py for historical portability
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 29570
diff changeset
69 # foo,
d1a7d9c279bb tests: check importing modules in perf.py for historical portability
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 29570
diff changeset
70 # bar,
d1a7d9c279bb tests: check importing modules in perf.py for historical portability
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 29570
diff changeset
71 # baz
d1a7d9c279bb tests: check importing modules in perf.py for historical portability
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 29570
diff changeset
72 # )
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42138
diff changeset
73 (
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42138
diff changeset
74 (
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42138
diff changeset
75 r'from mercurial import [(][a-z0-9, \n#]*\n(?! *%s,|^[ #]*\n|[)])'
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42138
diff changeset
76 % ',| *'.join(whitelist)
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42138
diff changeset
77 ),
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42138
diff changeset
78 "import newer module separately in try clause for early Mercurial",
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42138
diff changeset
79 )
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42138
diff changeset
80 )
29571
d1a7d9c279bb tests: check importing modules in perf.py for historical portability
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents: 29570
diff changeset
81
29570
cbd240188e4e tests: introduce check-perf-code.py to add extra checks on perf.py
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
diff changeset
82 # import contrib/check-code.py as checkcode
cbd240188e4e tests: introduce check-perf-code.py to add extra checks on perf.py
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
diff changeset
83 assert 'RUNTESTDIR' in os.environ, "use check-perf-code.py in *.t script"
cbd240188e4e tests: introduce check-perf-code.py to add extra checks on perf.py
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
diff changeset
84 contribpath = os.path.join(os.environ['RUNTESTDIR'], '..', 'contrib')
cbd240188e4e tests: introduce check-perf-code.py to add extra checks on perf.py
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
diff changeset
85 sys.path.insert(0, contribpath)
cbd240188e4e tests: introduce check-perf-code.py to add extra checks on perf.py
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
diff changeset
86 checkcode = __import__('check-code')
cbd240188e4e tests: introduce check-perf-code.py to add extra checks on perf.py
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
diff changeset
87
cbd240188e4e tests: introduce check-perf-code.py to add extra checks on perf.py
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
diff changeset
88 # register perf.py specific entry with "checks" in check-code.py
43076
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42138
diff changeset
89 checkcode.checks.append(
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42138
diff changeset
90 ('perf.py', r'contrib/perf.py$', '', checkcode.pyfilters, perfpypats)
2372284d9457 formatting: blacken the codebase
Augie Fackler <augie@google.com>
parents: 42138
diff changeset
91 )
29570
cbd240188e4e tests: introduce check-perf-code.py to add extra checks on perf.py
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
diff changeset
92
cbd240188e4e tests: introduce check-perf-code.py to add extra checks on perf.py
FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
parents:
diff changeset
93 sys.exit(checkcode.main())