annotate rust/hg-core/src/testing.rs @ 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 168041fa6d5f
children 4c5f6e95df84
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
41241
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
1 // testing.rs
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
2 //
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
3 // Copyright 2018 Georges Racinet <georges.racinet@octobus.net>
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
4 //
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
5 // This software may be used and distributed according to the terms of the
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
6 // GNU General Public License version 2 or any later version.
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
7
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
8 use crate::{Graph, GraphError, Revision, NULL_REVISION};
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
9
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
10 /// A stub `Graph`, same as the one from `test-ancestor.py`
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
11 ///
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
12 /// o 13
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
13 /// |
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
14 /// | o 12
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
15 /// | |
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
16 /// | | o 11
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
17 /// | | |\
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
18 /// | | | | o 10
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
19 /// | | | | |
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
20 /// | o---+ | 9
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
21 /// | | | | |
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
22 /// o | | | | 8
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
23 /// / / / /
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
24 /// | | o | 7
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
25 /// | | | |
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
26 /// o---+ | 6
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
27 /// / / /
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
28 /// | | o 5
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
29 /// | |/
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
30 /// | o 4
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
31 /// | |
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
32 /// o | 3
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
33 /// | |
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
34 /// | o 2
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
35 /// |/
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
36 /// o 1
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
37 /// |
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
38 /// o 0
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
39 #[derive(Clone, Debug)]
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
40 pub struct SampleGraph;
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
41
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
42 impl Graph for SampleGraph {
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
43 fn parents(&self, rev: Revision) -> Result<[Revision; 2], GraphError> {
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
44 match rev {
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
45 0 => Ok([NULL_REVISION, NULL_REVISION]),
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
46 1 => Ok([0, NULL_REVISION]),
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
47 2 => Ok([1, NULL_REVISION]),
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
48 3 => Ok([1, NULL_REVISION]),
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
49 4 => Ok([2, NULL_REVISION]),
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
50 5 => Ok([4, NULL_REVISION]),
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
51 6 => Ok([4, NULL_REVISION]),
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
52 7 => Ok([4, NULL_REVISION]),
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
53 8 => Ok([NULL_REVISION, NULL_REVISION]),
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
54 9 => Ok([6, 7]),
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
55 10 => Ok([5, NULL_REVISION]),
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
56 11 => Ok([3, 7]),
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
57 12 => Ok([9, NULL_REVISION]),
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
58 13 => Ok([8, NULL_REVISION]),
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
59 r => Err(GraphError::ParentOutOfRange(r)),
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
60 }
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
61 }
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
62 }
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
63
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
64 // A Graph represented by a vector whose indices are revisions
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
65 // and values are parents of the revisions
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
66 pub type VecGraph = Vec<[Revision; 2]>;
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
67
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
68 impl Graph for VecGraph {
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
69 fn parents(&self, rev: Revision) -> Result<[Revision; 2], GraphError> {
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
70 Ok(self[rev as usize])
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
71 }
168041fa6d5f rust: factorized testing Graphs
Georges Racinet <georges.racinet@octobus.net>
parents:
diff changeset
72 }