branchmap: make branchcache responsible for reading
authorMartijn Pieters <mj@octobus.net>
Mon, 21 Jan 2019 16:04:48 +0000
changeset 41565 bf7fb97aecf1
parent 41564 a5493a251ad3
child 41566 eb7ce452e0fb
branchmap: make branchcache responsible for reading Encapsulate reading in a classmethod, to make it clear what kind of object is being handled. This is part of a stack of refactoring changes to help performance improvements down the line. Differential Revision: https://phab.mercurial-scm.org/D5635
contrib/perf.py
mercurial/branchmap.py
--- a/contrib/perf.py	Mon Feb 04 09:10:07 2019 -0800
+++ b/contrib/perf.py	Mon Jan 21 16:04:48 2019 +0000
@@ -2409,10 +2409,15 @@
         # add unfiltered
         allfilters.append(None)
 
-    branchcacheread = safeattrsetter(branchmap, b'read')
+    if util.safehasattr(branchmap.branchcache, 'fromfile'):
+        branchcacheread = safeattrsetter(branchmap.branchcache, b'fromfile')
+        branchcacheread.set(classmethod(lambda *args: None))
+    else:
+        # older versions
+        branchcacheread = safeattrsetter(branchmap, b'read')
+        branchcacheread.set(lambda *args: None)
     branchcachewrite = safeattrsetter(branchmap.branchcache, b'write')
-    branchcacheread.set(lambda repo: None)
-    branchcachewrite.set(lambda bc, repo: None)
+    branchcachewrite.set(lambda *args: None)
     try:
         for name in allfilters:
             printname = name
@@ -2556,9 +2561,15 @@
 
     repo.branchmap() # make sure we have a relevant, up to date branchmap
 
+    try:
+        fromfile = branchmap.branchcache.fromfile
+    except AttributeError:
+        # older versions
+        fromfile = branchmap.read
+
     currentfilter = filter
     # try once without timer, the filter may not be cached
-    while branchmap.read(repo) is None:
+    while fromfile(repo) is None:
         currentfilter = subsettable.get(currentfilter)
         if currentfilter is None:
             raise error.Abort(b'No branchmap cached for %s repo'
@@ -2569,7 +2580,7 @@
         if clearrevlogs:
             clearchangelog(repo)
     def bench():
-        branchmap.read(repo)
+        fromfile(repo)
     timer(bench, setup=setup)
     fm.end()
 
--- a/mercurial/branchmap.py	Mon Feb 04 09:10:07 2019 -0800
+++ b/mercurial/branchmap.py	Mon Jan 21 16:04:48 2019 +0000
@@ -30,63 +30,6 @@
 pack_into = struct.pack_into
 unpack_from = struct.unpack_from
 
-def _filename(repo):
-    """name of a branchcache file for a given repo or repoview"""
-    filename = "branch2"
-    if repo.filtername:
-        filename = '%s-%s' % (filename, repo.filtername)
-    return filename
-
-def read(repo):
-    f = None
-    try:
-        f = repo.cachevfs(_filename(repo))
-        lineiter = iter(f)
-        cachekey = next(lineiter).rstrip('\n').split(" ", 2)
-        last, lrev = cachekey[:2]
-        last, lrev = bin(last), int(lrev)
-        filteredhash = None
-        if len(cachekey) > 2:
-            filteredhash = bin(cachekey[2])
-        bcache = branchcache(tipnode=last, tiprev=lrev,
-                              filteredhash=filteredhash)
-        if not bcache.validfor(repo):
-            # invalidate the cache
-            raise ValueError(r'tip differs')
-        cl = repo.changelog
-        for l in lineiter:
-            l = l.rstrip('\n')
-            if not l:
-                continue
-            node, state, label = l.split(" ", 2)
-            if state not in 'oc':
-                raise ValueError(r'invalid branch state')
-            label = encoding.tolocal(label.strip())
-            node = bin(node)
-            if not cl.hasnode(node):
-                raise ValueError(
-                    r'node %s does not exist' % pycompat.sysstr(hex(node)))
-            bcache.setdefault(label, []).append(node)
-            if state == 'c':
-                bcache._closednodes.add(node)
-
-    except (IOError, OSError):
-        return None
-
-    except Exception as inst:
-        if repo.ui.debugflag:
-            msg = 'invalid branchheads cache'
-            if repo.filtername is not None:
-                msg += ' (%s)' % repo.filtername
-            msg += ': %s\n'
-            repo.ui.debug(msg % pycompat.bytestr(inst))
-        bcache = None
-
-    finally:
-        if f:
-            f.close()
-
-    return bcache
 
 ### Nearest subset relation
 # Nearest subset of filter X is a filter Y so that:
@@ -107,7 +50,7 @@
 
     revs = []
     if bcache is None or not bcache.validfor(repo):
-        bcache = read(repo)
+        bcache = branchcache.fromfile(repo)
         if bcache is None:
             subsetname = subsettable.get(filtername)
             if subsetname is None:
@@ -181,6 +124,64 @@
     This field can be used to avoid changelog reads when determining if a
     branch head closes a branch or not.
     """
+    @classmethod
+    def fromfile(cls, repo):
+        f = None
+        try:
+            f = repo.cachevfs(cls._filename(repo))
+            lineiter = iter(f)
+            cachekey = next(lineiter).rstrip('\n').split(" ", 2)
+            last, lrev = cachekey[:2]
+            last, lrev = bin(last), int(lrev)
+            filteredhash = None
+            if len(cachekey) > 2:
+                filteredhash = bin(cachekey[2])
+            bcache = cls(tipnode=last, tiprev=lrev, filteredhash=filteredhash)
+            if not bcache.validfor(repo):
+                # invalidate the cache
+                raise ValueError(r'tip differs')
+            cl = repo.changelog
+            for line in lineiter:
+                line = line.rstrip('\n')
+                if not line:
+                    continue
+                node, state, label = line.split(" ", 2)
+                if state not in 'oc':
+                    raise ValueError(r'invalid branch state')
+                label = encoding.tolocal(label.strip())
+                node = bin(node)
+                if not cl.hasnode(node):
+                    raise ValueError(
+                        r'node %s does not exist' % pycompat.sysstr(hex(node)))
+                bcache.setdefault(label, []).append(node)
+                if state == 'c':
+                    bcache._closednodes.add(node)
+
+        except (IOError, OSError):
+            return None
+
+        except Exception as inst:
+            if repo.ui.debugflag:
+                msg = 'invalid branchheads cache'
+                if repo.filtername is not None:
+                    msg += ' (%s)' % repo.filtername
+                msg += ': %s\n'
+                repo.ui.debug(msg % pycompat.bytestr(inst))
+            bcache = None
+
+        finally:
+            if f:
+                f.close()
+
+        return bcache
+
+    @staticmethod
+    def _filename(repo):
+        """name of a branchcache file for a given repo or repoview"""
+        filename = "branch2"
+        if repo.filtername:
+            filename = '%s-%s' % (filename, repo.filtername)
+        return filename
 
     def __init__(self, entries=(), tipnode=nullid, tiprev=nullrev,
                  filteredhash=None, closednodes=None):
@@ -246,7 +247,7 @@
 
     def write(self, repo):
         try:
-            f = repo.cachevfs(_filename(repo), "w", atomictemp=True)
+            f = repo.cachevfs(self._filename(repo), "w", atomictemp=True)
             cachekey = [hex(self.tipnode), '%d' % self.tiprev]
             if self.filteredhash is not None:
                 cachekey.append(hex(self.filteredhash))