Mercurial > hg
comparison mercurial/manifest.py @ 46095:93e09d370003
treemanifest: stop storing full path for each item in manifest._lazydirs
This information is obtainable, if needed, based on the lazydirs key (which is
the entry name) and the manifest's `dir()` method.
### Performance
This is actually both a memory and a performance improvement, but it's likely to
be a very small one in most situations. In the pathological repo I've been using
for testing other performance work I've done recently, this reduced the time for
a rebase operation (rebasing two commits across a public-phase change that
touches a sibling of one of my tracked directories where the common parent is
massive (>>10k entries)):
#### Before
```
Time (mean ± σ): 4.059 s ± 0.121 s [User: 0.9 ms, System: 0.6 ms]
Range (min … max): 3.941 s … 4.352 s 10 runs
```
#### After
```
Time (mean ± σ): 3.707 s ± 0.060 s [User: 0.8 ms, System: 0.8 ms]
Range (min … max): 3.648 s … 3.818 s 10 runs
```
Differential Revision: https://phab.mercurial-scm.org/D9553
author | Kyle Lippincott <spectral@google.com> |
---|---|
date | Thu, 03 Dec 2020 14:39:39 -0800 |
parents | 89a2afe31e82 |
children | a3ccbac659d8 |
comparison
equal
deleted
inserted
replaced
46094:1ced08423d59 | 46095:93e09d370003 |
---|---|
816 def _subpath(self, path): | 816 def _subpath(self, path): |
817 return self._dir + path | 817 return self._dir + path |
818 | 818 |
819 def _loadalllazy(self): | 819 def _loadalllazy(self): |
820 selfdirs = self._dirs | 820 selfdirs = self._dirs |
821 for d, (path, node, readsubtree, docopy) in pycompat.iteritems( | 821 subpath = self._subpath |
822 for d, (node, readsubtree, docopy) in pycompat.iteritems( | |
822 self._lazydirs | 823 self._lazydirs |
823 ): | 824 ): |
824 if docopy: | 825 if docopy: |
825 selfdirs[d] = readsubtree(path, node).copy() | 826 selfdirs[d] = readsubtree(subpath(d), node).copy() |
826 else: | 827 else: |
827 selfdirs[d] = readsubtree(path, node) | 828 selfdirs[d] = readsubtree(subpath(d), node) |
828 self._lazydirs = {} | 829 self._lazydirs = {} |
829 | 830 |
830 def _loadlazy(self, d): | 831 def _loadlazy(self, d): |
831 v = self._lazydirs.get(d) | 832 v = self._lazydirs.get(d) |
832 if v: | 833 if v: |
833 path, node, readsubtree, docopy = v | 834 node, readsubtree, docopy = v |
834 if docopy: | 835 if docopy: |
835 self._dirs[d] = readsubtree(path, node).copy() | 836 self._dirs[d] = readsubtree(self._subpath(d), node).copy() |
836 else: | 837 else: |
837 self._dirs[d] = readsubtree(path, node) | 838 self._dirs[d] = readsubtree(self._subpath(d), node) |
838 del self._lazydirs[d] | 839 del self._lazydirs[d] |
839 | 840 |
840 def _loadchildrensetlazy(self, visit): | 841 def _loadchildrensetlazy(self, visit): |
841 if not visit: | 842 if not visit: |
842 return None | 843 return None |
859 differs, load it in both | 860 differs, load it in both |
860 """ | 861 """ |
861 toloadlazy = [] | 862 toloadlazy = [] |
862 for d, v1 in pycompat.iteritems(t1._lazydirs): | 863 for d, v1 in pycompat.iteritems(t1._lazydirs): |
863 v2 = t2._lazydirs.get(d) | 864 v2 = t2._lazydirs.get(d) |
864 if not v2 or v2[1] != v1[1]: | 865 if not v2 or v2[0] != v1[0]: |
865 toloadlazy.append(d) | 866 toloadlazy.append(d) |
866 for d, v1 in pycompat.iteritems(t2._lazydirs): | 867 for d, v1 in pycompat.iteritems(t2._lazydirs): |
867 if d not in t1._lazydirs: | 868 if d not in t1._lazydirs: |
868 toloadlazy.append(d) | 869 toloadlazy.append(d) |
869 | 870 |
1090 if self._copyfunc is _noop: | 1091 if self._copyfunc is _noop: |
1091 | 1092 |
1092 def _copyfunc(s): | 1093 def _copyfunc(s): |
1093 self._load() | 1094 self._load() |
1094 s._lazydirs = { | 1095 s._lazydirs = { |
1095 d: (p, n, r, True) | 1096 d: (n, r, True) |
1096 for d, (p, n, r, c) in pycompat.iteritems(self._lazydirs) | 1097 for d, (n, r, c) in pycompat.iteritems(self._lazydirs) |
1097 } | 1098 } |
1098 sdirs = s._dirs | 1099 sdirs = s._dirs |
1099 for d, v in pycompat.iteritems(self._dirs): | 1100 for d, v in pycompat.iteritems(self._dirs): |
1100 sdirs[d] = v.copy() | 1101 sdirs[d] = v.copy() |
1101 s._files = dict.copy(self._files) | 1102 s._files = dict.copy(self._files) |
1315 def unmodifiedsince(self, m2): | 1316 def unmodifiedsince(self, m2): |
1316 return not self._dirty and not m2._dirty and self._node == m2._node | 1317 return not self._dirty and not m2._dirty and self._node == m2._node |
1317 | 1318 |
1318 def parse(self, text, readsubtree): | 1319 def parse(self, text, readsubtree): |
1319 selflazy = self._lazydirs | 1320 selflazy = self._lazydirs |
1320 subpath = self._subpath | |
1321 for f, n, fl in _parse(text): | 1321 for f, n, fl in _parse(text): |
1322 if fl == b't': | 1322 if fl == b't': |
1323 f = f + b'/' | 1323 f = f + b'/' |
1324 # False below means "doesn't need to be copied" and can use the | 1324 # False below means "doesn't need to be copied" and can use the |
1325 # cached value from readsubtree directly. | 1325 # cached value from readsubtree directly. |
1326 selflazy[f] = (subpath(f), n, readsubtree, False) | 1326 selflazy[f] = (n, readsubtree, False) |
1327 elif b'/' in f: | 1327 elif b'/' in f: |
1328 # This is a flat manifest, so use __setitem__ and setflag rather | 1328 # This is a flat manifest, so use __setitem__ and setflag rather |
1329 # than assigning directly to _files and _flags, so we can | 1329 # than assigning directly to _files and _flags, so we can |
1330 # assign a path in a subdirectory, and to mark dirty (compared | 1330 # assign a path in a subdirectory, and to mark dirty (compared |
1331 # to nullid). | 1331 # to nullid). |
1349 any submanifests have been written first, so their nodeids are correct. | 1349 any submanifests have been written first, so their nodeids are correct. |
1350 """ | 1350 """ |
1351 self._load() | 1351 self._load() |
1352 flags = self.flags | 1352 flags = self.flags |
1353 lazydirs = [ | 1353 lazydirs = [ |
1354 (d[:-1], v[1], b't') for d, v in pycompat.iteritems(self._lazydirs) | 1354 (d[:-1], v[0], b't') for d, v in pycompat.iteritems(self._lazydirs) |
1355 ] | 1355 ] |
1356 dirs = [(d[:-1], self._dirs[d]._node, b't') for d in self._dirs] | 1356 dirs = [(d[:-1], self._dirs[d]._node, b't') for d in self._dirs] |
1357 files = [(f, self._files[f], flags(f)) for f in self._files] | 1357 files = [(f, self._files[f], flags(f)) for f in self._files] |
1358 return _text(sorted(dirs + files + lazydirs)) | 1358 return _text(sorted(dirs + files + lazydirs)) |
1359 | 1359 |
1371 emptytree = treemanifest() | 1371 emptytree = treemanifest() |
1372 | 1372 |
1373 def getnode(m, d): | 1373 def getnode(m, d): |
1374 ld = m._lazydirs.get(d) | 1374 ld = m._lazydirs.get(d) |
1375 if ld: | 1375 if ld: |
1376 return ld[1] | 1376 return ld[0] |
1377 return m._dirs.get(d, emptytree)._node | 1377 return m._dirs.get(d, emptytree)._node |
1378 | 1378 |
1379 # let's skip investigating things that `match` says we do not need. | 1379 # let's skip investigating things that `match` says we do not need. |
1380 visit = match.visitchildrenset(self._dir[:-1]) | 1380 visit = match.visitchildrenset(self._dir[:-1]) |
1381 visit = self._loadchildrensetlazy(visit) | 1381 visit = self._loadchildrensetlazy(visit) |