changeset 25185:bf6b476f3b36

treemanifest: cache directory logs and manifests Since manifests instances are cached on the manifest log instance, we can cache directory manifests by caching the directory manifest logs. The directory manifest log cache is a plain dict, so it never expires; we assume that we can keep all the directories in memory. The cache is kept on the root manifestlog, so access to directory manifest logs now has to go through the root manifest log. The caching will soon not be only an optimization. When we start lazily loading directory manifests, we need to make sure we don't create multiple instances of the log objects. The caching takes care of that problem.
author Martin von Zweigbergk <martinvonz@google.com>
date Fri, 10 Apr 2015 23:12:33 -0700
parents 819cd397e306
children 80c5b2666a96
files mercurial/localrepo.py mercurial/manifest.py
diffstat 2 files changed, 20 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/localrepo.py	Mon May 18 15:40:23 2015 -0500
+++ b/mercurial/localrepo.py	Fri Apr 10 23:12:33 2015 -0700
@@ -461,7 +461,7 @@
         return manifest.manifest(self.svfs)
 
     def dirlog(self, dir):
-        return manifest.manifest(self.svfs, dir)
+        return self.manifest.dirlog(dir)
 
     @repofilecache('dirstate')
     def dirstate(self):
--- a/mercurial/manifest.py	Mon May 18 15:40:23 2015 -0500
+++ b/mercurial/manifest.py	Fri Apr 10 23:12:33 2015 -0700
@@ -796,7 +796,11 @@
             writesubtree(subm, subp1, subp2)
 
 class manifest(revlog.revlog):
-    def __init__(self, opener, dir=''):
+    def __init__(self, opener, dir='', dirlogcache=None):
+        '''The 'dir' and 'dirlogcache' arguments are for internal use by
+        manifest.manifest only. External users should create a root manifest
+        log with manifest.manifest(opener) and call dirlog() on it.
+        '''
         # During normal operations, we expect to deal with not more than four
         # revs at a time (such as during commit --amend). When rebasing large
         # stacks of commits, the number can go up, hence the config knob below.
@@ -820,12 +824,24 @@
             indexfile = "meta/" + dir + "00manifest.i"
         revlog.revlog.__init__(self, opener, indexfile)
         self._dir = dir
+        # The dirlogcache is kept on the root manifest log
+        if dir:
+            self._dirlogcache = dirlogcache
+        else:
+            self._dirlogcache = {'': self}
 
     def _newmanifest(self, data=''):
         if self._treeinmem:
             return treemanifest(self._dir, data)
         return manifestdict(data)
 
+    def dirlog(self, dir):
+        assert self._treeondisk
+        if dir not in self._dirlogcache:
+            self._dirlogcache[dir] = manifest(self.opener, dir,
+                                              self._dirlogcache)
+        return self._dirlogcache[dir]
+
     def _slowreaddelta(self, node):
         r0 = self.deltaparent(self.rev(node))
         m0 = self.read(self.node(r0))
@@ -867,8 +883,7 @@
         text = self.revision(node)
         if self._treeondisk:
             def readsubtree(dir, subm):
-                sublog = manifest(self.opener, dir)
-                return sublog.read(subm)
+                return self.dirlog(dir).read(subm)
             m = self._newmanifest()
             m.parse(text, readsubtree)
             m.setnode(node)
@@ -929,7 +944,7 @@
 
     def _addtree(self, m, transaction, link, m1, m2):
         def writesubtree(subm, subp1, subp2):
-            sublog = manifest(self.opener, subm.dir())
+            sublog = self.dirlog(subm.dir())
             sublog.add(subm, transaction, link, subp1, subp2, None, None)
         m.writesubtrees(m1, m2, writesubtree)
         text = m.dirtext(self._usemanifestv2)