Mercurial > hg
comparison mercurial/repoview.py @ 24565:2f7cb6e6acdd
repoview: improve compute staticblockers perf
Previously we would compute the repoview's static blockers by finding all the
children of hidden commits that were not hidden. This was O(number of commits
since first hidden change) since 'children' requires walking every commit from
tip until the first hidden change.
The new algorithm walks all heads down until it sees a public commit. This makes
the computation O(number of draft) commits, which is much faster in large
repositories with a large number of commits and a low number of drafts.
On a large repo with 1000+ obsolete markers and the earliest draft commit around
tip~200000, this improves computehidden perf by 200x (2s to 0.01s).
author | Durham Goode <durham@fb.com> |
---|---|
date | Wed, 01 Apr 2015 12:50:10 -0700 |
parents | a41902aac76d |
children | 9e558b788daa |
comparison
equal
deleted
inserted
replaced
24564:5ec4bda3097a | 24565:2f7cb6e6acdd |
---|---|
4 # Logilab SA <contact@logilab.fr> | 4 # Logilab SA <contact@logilab.fr> |
5 # | 5 # |
6 # This software may be used and distributed according to the terms of the | 6 # This software may be used and distributed according to the terms of the |
7 # GNU General Public License version 2 or any later version. | 7 # GNU General Public License version 2 or any later version. |
8 | 8 |
9 import collections | |
9 import copy | 10 import copy |
10 import error | 11 import error |
11 import phases | 12 import phases |
12 import util | 13 import util |
13 import obsolete | 14 import obsolete |
14 import struct | 15 import struct |
15 import tags as tagsmod | 16 import tags as tagsmod |
17 from node import nullrev | |
16 | 18 |
17 def hideablerevs(repo): | 19 def hideablerevs(repo): |
18 """Revisions candidates to be hidden | 20 """Revisions candidates to be hidden |
19 | 21 |
20 This is a standalone function to help extensions to wrap it.""" | 22 This is a standalone function to help extensions to wrap it.""" |
21 return obsolete.getrevs(repo, 'obsolete') | 23 return obsolete.getrevs(repo, 'obsolete') |
22 | 24 |
23 def _getstaticblockers(repo): | 25 def _getstatichidden(repo): |
24 """Cacheable revisions blocking hidden changesets from being filtered. | 26 """Cacheable revisions blocking hidden changesets from being filtered. |
25 | 27 |
26 Additional non-cached hidden blockers are computed in _getdynamicblockers. | 28 Additional non-cached hidden blockers are computed in _getdynamicblockers. |
27 This is a standalone function to help extensions to wrap it.""" | 29 This is a standalone function to help extensions to wrap it.""" |
28 assert not repo.changelog.filteredrevs | 30 assert not repo.changelog.filteredrevs |
29 hideable = hideablerevs(repo) | 31 hideable = hideablerevs(repo) |
30 blockers = set() | |
31 if hideable: | 32 if hideable: |
32 # We use cl to avoid recursive lookup from repo[xxx] | 33 actuallyhidden = {} |
33 cl = repo.changelog | 34 getphase = repo._phasecache.phase |
34 firsthideable = min(hideable) | 35 getparentrevs = repo.changelog.parentrevs |
35 revs = cl.revs(start=firsthideable) | 36 queue = collections.deque((r, False) for r in repo.changelog.headrevs()) |
36 tofilter = repo.revs( | 37 while queue: |
37 '(%ld) and children(%ld)', list(revs), list(hideable)) | 38 rev, blocked = queue.popleft() |
38 blockers.update([r for r in tofilter if r not in hideable]) | 39 phase = getphase(repo, rev) |
39 return blockers | 40 # Skip nodes which are public (guaranteed to not be hidden) and |
41 # nodes which have already been processed and won't be blocked by | |
42 # the previous node. | |
43 if phase == 0 or (not blocked and rev in actuallyhidden): | |
44 continue | |
45 if rev in hideable: | |
46 if blocked: | |
47 actuallyhidden[rev] = False | |
48 else: | |
49 actuallyhidden.setdefault(rev, True) | |
50 else: | |
51 blocked = True | |
52 | |
53 for parent in (p for p in getparentrevs(rev) if p != nullrev): | |
54 queue.append((parent, blocked)) | |
55 return set(rev for rev, hidden in actuallyhidden.iteritems() if hidden) | |
40 | 56 |
41 def _getdynamicblockers(repo): | 57 def _getdynamicblockers(repo): |
42 """Non-cacheable revisions blocking hidden changesets from being filtered. | 58 """Non-cacheable revisions blocking hidden changesets from being filtered. |
43 | 59 |
44 Get revisions that will block hidden changesets and are likely to change, | 60 Get revisions that will block hidden changesets and are likely to change, |
135 hideable = hideablerevs(repo) | 151 hideable = hideablerevs(repo) |
136 if hideable: | 152 if hideable: |
137 cl = repo.changelog | 153 cl = repo.changelog |
138 hidden = tryreadcache(repo, hideable) | 154 hidden = tryreadcache(repo, hideable) |
139 if hidden is None: | 155 if hidden is None: |
140 blocked = cl.ancestors(_getstaticblockers(repo), inclusive=True) | 156 hidden = frozenset(_getstatichidden(repo)) |
141 hidden = frozenset(r for r in hideable if r not in blocked) | |
142 trywritehiddencache(repo, hideable, hidden) | 157 trywritehiddencache(repo, hideable, hidden) |
143 | 158 |
144 # check if we have wd parents, bookmarks or tags pointing to hidden | 159 # check if we have wd parents, bookmarks or tags pointing to hidden |
145 # changesets and remove those. | 160 # changesets and remove those. |
146 dynamic = hidden & _getdynamicblockers(repo) | 161 dynamic = hidden & _getdynamicblockers(repo) |