hidden: simplify the computation of consistency blocker

For a couple of years, we now have precomputed set for all mutable phases. We
can use this set restrict our search and quickly detect non-hideable children of
hideable changesets.  This speeds up the hidden computation. See docstring of
the new function for details.

This new version reuses the '_domainancestors' function to keep the computation
of revealed changeset in O(len(visible))

Below are perfvolatilesets timing from two Mozilla repositories with different
contents. hidden cache is disabled while obtaining them.

1) Mozilla repository with:
 * 400667 changesets
 * 35 hidden changesets (first rev-268334)
 * 288 visible drafts
 * 1 unstable changeset

Before:
! visible
! wall 0.001744 comb 0.000000 user 0.000000 sys 0.000000 (best of 1563)

After:
! visible
! wall 0.000742 comb 0.000000 user 0.000000 sys 0.000000 (best of 3755)


The timing above include the computation of obsolete changeset:
! obsolete
! wall 0.000396 comb 0.000000 user 0.000000 sys 0.000000 (best of 6816)

So adjusted time give 1.3ms before versus 0.3ms after. A 4x speedup.

2) Mozilla repository with:
 * 405645 changesets
 * 4312 hidden changesets (first rev-326004)
 * 264 visible drafts
 * 1 unstable changeset

Before:
! visible
! wall 0.025476 comb 0.030000 user 0.030000 sys 0.000000 (best of 111)


After
! visible
! wall 0.007703 comb 0.010000 user 0.010000 sys 0.000000 (best of 358)


The timing above include the computation of obsolete changeset:
! obsolete
! wall 0.006408 comb 0.010000 user 0.010000 sys 0.000000 (best of 404)

So adjusted time give 19ms before versus 1.3ms after. A 17x speedup.
This commit is contained in:
Pierre-Yves David 2017-05-21 16:01:20 +02:00
parent 9d76dff252
commit 103db3f051

View File

@ -10,7 +10,6 @@ from __future__ import absolute_import
import copy
import hashlib
import heapq
import struct
from .node import nullrev
@ -63,35 +62,33 @@ def _getstatichidden(repo):
"""
assert not repo.changelog.filteredrevs
hidden = set(hideablerevs(repo))
hidden = hideablerevs(repo)
if hidden:
getphase = repo._phasecache.phase
getparentrevs = repo.changelog.parentrevs
# Skip heads which are public (guaranteed to not be hidden)
heap = [-r for r in repo.changelog.headrevs() if getphase(repo, r)]
heapq.heapify(heap)
heappop = heapq.heappop
heappush = heapq.heappush
seen = set() # no need to init it with heads, they have no children
while heap:
rev = -heappop(heap)
# All children have been processed so at that point, if no children
# removed 'rev' from the 'hidden' set, 'rev' is going to be hidden.
blocker = rev not in hidden
for parent in getparentrevs(rev):
if parent == nullrev:
continue
if blocker:
# If visible, ensure parent will be visible too
hidden.discard(parent)
# - Avoid adding the same revision twice
# - Skip nodes which are public (guaranteed to not be hidden)
pre = len(seen)
seen.add(parent)
if pre < len(seen) and getphase(repo, rev):
heappush(heap, -parent)
pfunc = repo.changelog.parentrevs
mutablephases = (phases.draft, phases.secret)
mutable = repo._phasecache.getrevset(repo, mutablephases)
blockers = _consistencyblocker(pfunc, hidden, mutable)
if blockers:
hidden = hidden - _domainancestors(pfunc, blockers, mutable)
return hidden
def _consistencyblocker(pfunc, hideable, domain):
"""return non-hideable changeset blocking hideable one
For consistency, we cannot actually hide a changeset if one of it children
are visible, this function find such children.
"""
others = domain - hideable
blockers = set()
for r in others:
for p in pfunc(r):
if p != nullrev and p in hideable:
blockers.add(r)
break
return blockers
def _domainancestors(pfunc, revs, domain):
"""return ancestors of 'revs' within 'domain'