Patchwork [3,of,4] revset: changed descendants revset to use lazy generators

login
register
mail settings
Submitter Lucas Moscovicz
Date March 6, 2014, 11:19 p.m.
Message ID <f91f466dd6b238140ee9.1394147958@dev1037.prn2.facebook.com>
Download mbox | patch
Permalink /patch/3884/
State Accepted
Commit 7af341082b7685641a65aa809a76d4e54bb35a98
Headers show

Comments

Lucas Moscovicz - March 6, 2014, 11:19 p.m.
# HG changeset patch
# User Lucas Moscovicz <lmoscovicz@fb.com>
# Date 1392064005 28800
#      Mon Feb 10 12:26:45 2014 -0800
# Node ID f91f466dd6b238140ee96d523b7a2476d324c23c
# Parent  a2d473ebec3a547b505a02b47d0f8f0ac95ca197
revset: changed descendants revset to use lazy generators

Performance Benchmarking:

$ time hg log -qr "0:: and 0:5"
...

real  0m3.665s
user  0m3.364s
sys 0m0.289s

$ time ./hg log -qr "0:: and 0:5"
...

real  0m0.492s
user  0m0.394s
sys 0m0.097s

Patch

diff --git a/mercurial/revset.py b/mercurial/revset.py
--- a/mercurial/revset.py
+++ b/mercurial/revset.py
@@ -51,23 +51,26 @@ 
 def _revdescendants(repo, revs, followfirst):
     """Like revlog.descendants() but supports followfirst."""
     cut = followfirst and 1 or None
-    cl = repo.changelog
-    first = min(revs)
-    nullrev = node.nullrev
-    if first == nullrev:
-        # Are there nodes with a null first parent and a non-null
-        # second one? Maybe. Do we care? Probably not.
-        for i in cl:
-            yield i
-        return
 
-    seen = set(revs)
-    for i in cl.revs(first + 1):
-        for x in cl.parentrevs(i)[:cut]:
-            if x != nullrev and x in seen:
-                seen.add(i)
+    def iterate():
+        cl = repo.changelog
+        first = min(revs)
+        nullrev = node.nullrev
+        if first == nullrev:
+            # Are there nodes with a null first parent and a non-null
+            # second one? Maybe. Do we care? Probably not.
+            for i in cl:
                 yield i
-                break
+        else:
+            seen = set(revs)
+            for i in cl.revs(first + 1):
+                for x in cl.parentrevs(i)[:cut]:
+                    if x != nullrev and x in seen:
+                        seen.add(i)
+                        yield i
+                        break
+
+    return ascgeneratorset(iterate())
 
 def _revsbetween(repo, roots, heads):
     """Return all paths between roots and heads, inclusive of both endpoint
@@ -641,8 +644,9 @@ 
     args = getset(repo, spanset(repo), x)
     if not args:
         return baseset([])
-    s = set(_revdescendants(repo, args, followfirst)) | set(args)
-    return subset & s
+    s = _revdescendants(repo, args, followfirst)
+    a = set(args)
+    return subset.filter(lambda r: r in s or r in a)
 
 def descendants(repo, subset, x):
     """``descendants(set)``