comparison mercurial/revset.py @ 29304:5e32852fa4bd

revset: make filteredset.__nonzero__ respect the order of the filteredset This fix allows __nonzero__ to respect the direction of iteration of the whole filteredset. Here's the case when it matters. Imagine that we have a very large repository and we want to execute a command like: $ hg log --rev '(tip:0) and user(ikostia)' --limit 1 (we want to get the latest commit by me). Mercurial will evaluate a filteredset lazy data structure, an instance of the filteredset class, which will know that it has to iterate in a descending order (isdescending() will return True if called). This means that when some code iterates over the instance of this filteredset, the 'and user(ikostia)' condition will be first checked on the latest revision, then on the second latest and so on, allowing Mercurial to print matches as it founds them. However, cmdutil.getgraphlogrevs contains the following code: revs = _logrevs(repo, opts) if not revs: return revset.baseset(), None, None The "not revs" expression is evaluated by calling filteredset.__nonzero__, which in its current implementation will try to iterate the filteredset in ascending order until it finds a revision that matches the 'and user(..' condition. If the condition is only true on late revisions, a lot of useless iterations will be done. These iterations could be avoided if __nonzero__ followed the order of the filteredset, which in my opinion is a sensible thing to do here. The problem gets even worse when instead of 'user(ikostia)' some more expensive check is performed, like grepping the commit diff. I tested this fix on a very large repo where tip is my commit and my very first commit comes fairly late in the revision history. Results of timing of the above command on that very large repo. -with my fix: real 0m1.795s user 0m1.657s sys 0m0.135s -without my fix: real 1m29.245s user 1m28.223s sys 0m0.929s I understand that this is a very specific kind of problem that presents itself very rarely, only on very big repositories and with expensive checks and so on. But I don't see any disadvantages to this kind of fix either.
author Kostia Balytskyi <ikostia@fb.com>
date Thu, 02 Jun 2016 22:39:01 +0100
parents 3f9e68864ccc
children 38e0c83c7ee4
comparison
equal deleted inserted replaced
29303:3a2357c31d2a 29304:5e32852fa4bd
2741 if it is None: 2741 if it is None:
2742 return None 2742 return None
2743 return lambda: self._iterfilter(it()) 2743 return lambda: self._iterfilter(it())
2744 2744
2745 def __nonzero__(self): 2745 def __nonzero__(self):
2746 fast = self.fastasc 2746 fast = None
2747 if fast is None: 2747 candidates = [self.fastasc if self.isascending() else None,
2748 fast = self.fastdesc 2748 self.fastdesc if self.isdescending() else None,
2749 self.fastasc,
2750 self.fastdesc]
2751 for candidate in candidates:
2752 if candidate is not None:
2753 fast = candidate
2754 break
2755
2749 if fast is not None: 2756 if fast is not None:
2750 it = fast() 2757 it = fast()
2751 else: 2758 else:
2752 it = self 2759 it = self
2753 2760