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)