# HG changeset patch # User Jun Wu # Date 2017-02-18 04:59:29 # Node ID 1076f7eba96443e1693bacb25d5fe475450019a8 # Parent 219629d42786414b8b885c2daf52153ecbd5fe32 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. diff --git a/mercurial/smartset.py b/mercurial/smartset.py --- a/mercurial/smartset.py +++ b/mercurial/smartset.py @@ -171,8 +171,12 @@ class baseset(abstractsmartset): 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 @@ class baseset(abstractsmartset): 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 @@ class baseset(abstractsmartset): 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 @@ class baseset(abstractsmartset): 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.