smartset: convert set to list lazily
authorJun Wu <quark@fb.com>
Fri, 17 Feb 2017 20:59:29 -0800
changeset 31015 1076f7eba964
parent 31014 219629d42786
child 31016 bf81d3b7b2ba
smartset: convert set to list lazily If the caller only wants to construct a baseset via a set, and then do "__contains__" tests. It's unnecessary to initialize the list. Testing on my unfiltered hg-committed repo where len(draft()) is 2600, this patch shows about 6% improvement on set intensive queries: Before: $ for i in `seq 5`; hg perfrevset 'draft() & draft() & draft() & draft()' ! wall 0.001196 comb 0.000000 user 0.000000 sys 0.000000 (best of 2011) ! wall 0.001191 comb 0.000000 user 0.000000 sys 0.000000 (best of 2099) ! wall 0.001186 comb 0.010000 user 0.010000 sys 0.000000 (best of 1953) ! wall 0.001182 comb 0.000000 user 0.000000 sys 0.000000 (best of 2135) ! wall 0.001193 comb 0.000000 user 0.000000 sys 0.000000 (best of 2177) After: $ for i in `seq 5`; hg perfrevset 'draft() & draft() & draft() & draft()' ! wall 0.001128 comb 0.000000 user 0.000000 sys 0.000000 (best of 2247) ! wall 0.001119 comb 0.000000 user 0.000000 sys 0.000000 (best of 2317) ! wall 0.001115 comb 0.000000 user 0.000000 sys 0.000000 (best of 2244) ! wall 0.001131 comb 0.000000 user 0.000000 sys 0.000000 (best of 2093) ! wall 0.001124 comb 0.000000 user 0.000000 sys 0.000000 (best of 2134) It could have bigger impact on larger sets in theory.
mercurial/smartset.py
--- a/mercurial/smartset.py	Thu Feb 16 11:34:50 2017 -0500
+++ b/mercurial/smartset.py	Fri Feb 17 20:59:29 2017 -0800
@@ -171,8 +171,12 @@
                 self._set = data
                 # set has no order we pick one for stability purpose
                 self._ascending = True
-            data = list(data)
-        self._list = data
+                # converting set to list has a cost, do it lazily
+                data = None
+            else:
+                data = list(data)
+        if data is not None:
+            self._list = data
         self._datarepr = datarepr
 
     @util.propertycache
@@ -185,6 +189,12 @@
         asclist.sort()
         return asclist
 
+    @util.propertycache
+    def _list(self):
+        # _list is only lazily constructed if we have _set
+        assert '_set' in self.__dict__
+        return list(self._set)
+
     def __iter__(self):
         if self._ascending is None:
             return iter(self._list)
@@ -204,7 +214,7 @@
         return self._set.__contains__
 
     def __nonzero__(self):
-        return bool(self._list)
+        return bool(len(self))
 
     def sort(self, reverse=False):
         self._ascending = not bool(reverse)
@@ -218,7 +228,10 @@
         self._istopo = False
 
     def __len__(self):
-        return len(self._list)
+        if '_list' in self.__dict__:
+            return len(self._list)
+        else:
+            return len(self._set)
 
     def isascending(self):
         """Returns True if the collection is ascending order, False if not.