smartset: use native set operations as fast paths
authorJun Wu <quark@fb.com>
Sat, 18 Feb 2017 17:23:43 -0800
changeset 31020 2d1bf84046f6
parent 31019 74f77f1c2215
child 31021 4189d790e8a4
smartset: use native set operations as fast paths For set operations like "&" and "-", where we know both basesets have their sets ready, and the first set is sorted, use the native Python set operations as a fast path. Note: "+" is not optimized as that will break the ordering. This leads to noticeable improvements on performance: revset | before | after | delta ---------------------------------------------------------------- draft() & draft() & draft() & draft() | 776 | 477 | -39% draft() + draft() + draft() + draft() | 2849 | 2864 | draft() - draft() + draft() - draft() | 943 | 240 | -75% draft() - draft() - draft() - draft() | 557 | 197 | -64% (time measured in microseconds)
mercurial/smartset.py
--- a/mercurial/smartset.py	Sat Feb 18 16:30:07 2017 -0800
+++ b/mercurial/smartset.py	Sat Feb 18 17:23:43 2017 -0800
@@ -171,7 +171,7 @@
     >>> [list(i) for i in [xs + ys, xs & ys, xs - ys]]
     [[0, 4, 6, 7, 3, 5], [6, 7], [0, 4]]
     >>> [type(i).__name__ for i in [xs + ys, xs & ys, xs - ys]]
-    ['addset', 'filteredset', 'filteredset']
+    ['addset', 'baseset', 'baseset']
 
     Construct by a list-like:
     >>> xs = baseset(x)
@@ -191,18 +191,18 @@
     >>> [type(i).__name__ for i in [xs + ys, xs & ys, xs - ys]]
     ['addset', 'filteredset', 'filteredset']
 
-    With sort():
+    With sort(), set optimization could be used:
     >>> xs.sort(reverse=True)
     >>> [list(i) for i in [xs + ys, xs & ys, xs - ys]]
     [[7, 6, 4, 0, 5, 3], [7, 6], [4, 0]]
     >>> [type(i).__name__ for i in [xs + ys, xs & ys, xs - ys]]
-    ['addset', 'filteredset', 'filteredset']
+    ['addset', 'baseset', 'baseset']
 
     >>> ys.sort()
     >>> [list(i) for i in [xs + ys, xs & ys, xs - ys]]
     [[7, 6, 4, 0, 3, 5], [7, 6], [4, 0]]
     >>> [type(i).__name__ for i in [xs + ys, xs & ys, xs - ys]]
-    ['addset', 'filteredset', 'filteredset']
+    ['addset', 'baseset', 'baseset']
     """
     def __init__(self, data=(), datarepr=None, istopo=False):
         """
@@ -322,6 +322,22 @@
                 return self._asclist[0]
         return None
 
+    def _fastsetop(self, other, op):
+        # try to use native set operations as fast paths
+        if (type(other) is baseset and '_set' in other.__dict__ and '_set' in
+            self.__dict__ and self._ascending is not None):
+            s = baseset(data=getattr(self._set, op)(other._set))
+            s._ascending = self._ascending
+        else:
+            s = getattr(super(baseset, self), op)(other)
+        return s
+
+    def __and__(self, other):
+        return self._fastsetop(other, '__and__')
+
+    def __sub__(self, other):
+        return self._fastsetop(other, '__sub__')
+
     def __repr__(self):
         d = {None: '', False: '-', True: '+'}[self._ascending]
         s = _formatsetrepr(self._datarepr)