Patchwork [11,of,22] obsstore: generalize successors, precursors, children to a class

login
register
mail settings
Submitter Jun Wu
Date June 4, 2017, 11:59 p.m.
Message ID <69870e8f6bc97775c47d.1496620763@x1c>
Download mbox | patch
Permalink /patch/21189/
State Accepted
Headers show

Comments

Jun Wu - June 4, 2017, 11:59 p.m.
# HG changeset patch
# User Jun Wu <quark@fb.com>
# Date 1496558287 25200
#      Sat Jun 03 23:38:07 2017 -0700
# Node ID 69870e8f6bc97775c47d363efc5c58bce33239c0
# Parent  994e09024cdd5bbca80f1beb9dcbd2f1a19aa1bb
# Available At https://bitbucket.org/quark-zju/hg-draft
#              hg pull https://bitbucket.org/quark-zju/hg-draft -r 69870e8f6bc9
obsstore: generalize successors, precursors, children to a class

Add a "markerindex" class which takes a function to extract key from a
marker. Therefore "successors", "precursors", "children" can reuse the same
implementation by specifying different "keyfunc"s.

There is no noticeable perf change running "hg log -r ." in hg-committed.
I guess subclassing builtin dict is the key to gain good perf. Note the
"dict" base class is temporary and will be changed in a later patch.

Patch

diff --git a/mercurial/obsolete.py b/mercurial/obsolete.py
--- a/mercurial/obsolete.py
+++ b/mercurial/obsolete.py
@@ -72,4 +72,5 @@  from __future__ import absolute_import
 import errno
 import struct
+import weakref
 
 from .i18n import _
@@ -507,22 +508,26 @@  class marker(object):
         return self._data[2]
 
-@util.nogc
-def _addsuccessors(successors, markers):
-    for mark in markers:
-        successors.setdefault(mark[0], set()).add(mark)
+class markerindex(dict):
+    """key is usually a node, value is a set of markers"""
+
+    def __init__(self, obsstore, name, keyfunc):
+        """keyfunc: rawmarker -> [key]"""
+        self._keyfunc = keyfunc
+        self._obsstore = weakref.proxy(obsstore)
+        self.name = name
+        self.sourceoftruthsize = 0
+        self.update()
 
-@util.nogc
-def _addprecursors(precursors, markers):
-    for mark in markers:
-        for suc in mark[1]:
-            precursors.setdefault(suc, set()).add(mark)
-
-@util.nogc
-def _addchildren(children, markers):
-    for mark in markers:
-        parents = mark[5]
-        if parents is not None:
-            for p in parents:
-                children.setdefault(p, set()).add(mark)
+    @util.nogc
+    def update(self):
+        """read latest obsstore and build index for its markers"""
+        allmarkers = self._obsstore._all
+        keyfunc = self._keyfunc
+        setdefault = self.setdefault
+        for i in xrange(self.sourceoftruthsize, len(allmarkers)):
+            marker = allmarkers[i]
+            for k in keyfunc(marker):
+                setdefault(k, set()).add(marker)
+        self.sourceoftruthsize = len(allmarkers)
 
 def _checkinvalidmarkers(markers):
@@ -693,19 +698,13 @@  class obsstore(object):
     @propertycache
     def successors(self):
-        successors = {}
-        _addsuccessors(successors, self._all)
-        return successors
+        return markerindex(self, 'successors', lambda m: (m[0],))
 
     @propertycache
     def precursors(self):
-        precursors = {}
-        _addprecursors(precursors, self._all)
-        return precursors
+        return markerindex(self, 'precursors', lambda m: m[1])
 
     @propertycache
     def children(self):
-        children = {}
-        _addchildren(children, self._all)
-        return children
+        return markerindex(self, 'children', lambda m: m[5] or ())
 
     def _cached(self, attr):
@@ -716,10 +715,8 @@  class obsstore(object):
         self._data = self._data + rawdata
         self._all.extend(markers)
-        if self._cached('successors'):
-            _addsuccessors(self.successors, markers)
-        if self._cached('precursors'):
-            _addprecursors(self.precursors, markers)
-        if self._cached('children'):
-            _addchildren(self.children, markers)
+        for name in ('successors', 'precursors', 'children'):
+            index = self.__dict__.get(name, None)
+            if index is not None:
+                index.update()
         _checkinvalidmarkers(markers)