Patchwork revlog: optimize ancestors() to not check filtered revisions for each

login
register
mail settings
Submitter Yuya Nishihara
Date Oct. 12, 2018, 5:22 a.m.
Message ID <7bbc8f3037c847d44447.1539321732@mimosa>
Download mbox | patch
Permalink /patch/35647/
State Accepted
Headers show

Comments

Yuya Nishihara - Oct. 12, 2018, 5:22 a.m.
# HG changeset patch
# User Yuya Nishihara <yuya@tcha.org>
# Date 1539318163 -7200
#      Fri Oct 12 06:22:43 2018 +0200
# Node ID 7bbc8f3037c847d44447d0088610451a80f0aaeb
# Parent  74558635dad53eccbbe2e67df835df0873d5c870
revlog: optimize ancestors() to not check filtered revisions for each

While reviewing the Rust implementation, I noticed iter(ancestors) doesn't
need to check filtering state for each parent revision. And doing that appears
to have some measurable perf win.

  $ hg perfancestors -R mercurial
  (orig) wall 0.038093 comb 0.040000 user 0.040000 sys 0.000000 (best of 100)
  (this) wall 0.024795 comb 0.020000 user 0.020000 sys 0.000000 (best of 117)
Augie Fackler - Oct. 12, 2018, 7:49 a.m.
> On Oct 12, 2018, at 07:22, Yuya Nishihara <yuya@tcha.org> wrote:
> 
> # HG changeset patch
> # User Yuya Nishihara <yuya@tcha.org>
> # Date 1539318163 -7200
> #      Fri Oct 12 06:22:43 2018 +0200
> # Node ID 7bbc8f3037c847d44447d0088610451a80f0aaeb
> # Parent  74558635dad53eccbbe2e67df835df0873d5c870
> revlog: optimize ancestors() to not check filtered revisions for each

queued, thanks

Patch

diff --git a/mercurial/revlog.py b/mercurial/revlog.py
--- a/mercurial/revlog.py
+++ b/mercurial/revlog.py
@@ -648,6 +648,9 @@  class revlog(object):
 
         return entry[5], entry[6]
 
+    # fast parentrevs(rev) where rev isn't filtered
+    _uncheckedparentrevs = parentrevs
+
     def node(self, rev):
         try:
             return self.index[rev][7]
@@ -747,8 +750,14 @@  class revlog(object):
 
         See the documentation for ancestor.lazyancestors for more details."""
 
-        return ancestor.lazyancestors(self.parentrevs, revs, stoprev=stoprev,
-                                      inclusive=inclusive)
+        # first, make sure start revisions aren't filtered
+        revs = list(revs)
+        checkrev = self.node
+        for r in revs:
+            checkrev(r)
+        # and we're sure ancestors aren't filtered as well
+        return ancestor.lazyancestors(self._uncheckedparentrevs, revs,
+                                      stoprev=stoprev, inclusive=inclusive)
 
     def descendants(self, revs):
         return dagop.descendantrevs(revs, self.revs, self.parentrevs)