Mercurial > hg-stable
changeset 51933:8583d138f436
exchange: improve computation of relevant markers for large repos
Compute the candidate nodes with relevant markers directly
from keys of the predecessors/successors/children dictionaries of
obsstore. This is faster than iterating over all nodes directly.
This test could be further improved for repositories with relative
few markers compared to the repository size, but this is no longer
hot already. With the current loop structure, the obshashrange use
works as well as before as it passes lists with a single node.
Adjust the interface by allowing revision lists as well as node lists.
This helps cases that computes ancestors as it reduces the
materialisation cost. Use this in _pushdiscoveryobsmarker and
_getbundleobsmarkerpart. Improve the latter further by directly using
ancestors().
Performance benchmarks show notable and welcome improvement to no-op push and
pull (that would also apply to other push/pull). This apply to push and pull
done without evolve.
### push/pull Benchmark parameter
# bin-env-vars.hg.flavor = default
# benchmark.variants.explicit-rev = none
# benchmark.variants.protocol = ssh
# benchmark.variants.revs = none
## benchmark.name = hg.command.pull
# data-env-vars.name = mercurial-devel-2024-03-22-zstd-sparse-revlog
before: 5.968537 seconds
after: 5.668507 seconds (-5.03%, -0.30)
# data-env-vars.name = tryton-devel-2024-03-22-zstd-sparse-revlog
before: 1.446232 seconds
after: 0.835553 seconds (-42.23%, -0.61)
# data-env-vars.name = netbsd-src-draft-2024-09-19-zstd-sparse-revlog
before: 5.777412 seconds
after: 2.523454 seconds (-56.32%, -3.25)
## benchmark.name = hg.command.push
# data-env-vars.name = mercurial-devel-2024-03-22-zstd-sparse-revlog
before: 6.155501 seconds
after: 5.885072 seconds (-4.39%, -0.27)
# data-env-vars.name = tryton-devel-2024-03-22-zstd-sparse-revlog
before: 1.491054 seconds
after: 0.934882 seconds (-37.30%, -0.56)
# data-env-vars.name = netbsd-src-draft-2024-09-19-zstd-sparse-revlog
before: 5.902494 seconds
after: 2.957644 seconds (-49.89%, -2.94)
There is not notable different in these result using the "rust" flavor instead
of the "default". The performance impact on the same operation when using
evolve were also tested and no impact was noted.
author | Joerg Sonnenberger <joerg@bec.de> |
---|---|
date | Mon, 08 Jul 2024 22:46:04 +0200 |
parents | ee7e106b372b |
children | 499b19683c1b |
files | mercurial/bundle2.py mercurial/exchange.py mercurial/obsolete.py mercurial/obsutil.py |
diffstat | 4 files changed, 39 insertions(+), 13 deletions(-) [+] |
line wrap: on
line diff
--- a/mercurial/bundle2.py Fri Sep 20 21:31:58 2024 -0400 +++ b/mercurial/bundle2.py Mon Jul 08 22:46:04 2024 +0200 @@ -1804,7 +1804,7 @@ addpartrevbranchcache(repo, bundler, outgoing) if opts.get(b'obsolescence', False): - obsmarkers = repo.obsstore.relevantmarkers(outgoing.missing) + obsmarkers = repo.obsstore.relevantmarkers(nodes=outgoing.missing) buildobsmarkerspart( bundler, obsmarkers,
--- a/mercurial/exchange.py Fri Sep 20 21:31:58 2024 -0400 +++ b/mercurial/exchange.py Mon Jul 08 22:46:04 2024 +0200 @@ -704,8 +704,8 @@ repo = pushop.repo # very naive computation, that can be quite expensive on big repo. # However: evolution is currently slow on them anyway. - nodes = (c.node() for c in repo.set(b'::%ln', pushop.futureheads)) - pushop.outobsmarkers = pushop.repo.obsstore.relevantmarkers(nodes) + revs = repo.revs(b'::%ln', pushop.futureheads) + pushop.outobsmarkers = pushop.repo.obsstore.relevantmarkers(revs=revs) @pushdiscovery(b'bookmarks') @@ -2604,10 +2604,15 @@ ): """add an obsolescence markers part to the requested bundle""" if kwargs.get('obsmarkers', False): + unfi_cl = repo.unfiltered().changelog if heads is None: - heads = repo.heads() - subset = [c.node() for c in repo.set(b'::%ln', heads)] - markers = repo.obsstore.relevantmarkers(subset) + headrevs = repo.changelog.headrevs() + else: + get_rev = unfi_cl.index.get_rev + headrevs = [get_rev(node) for node in heads] + headrevs = [rev for rev in headrevs if rev is not None] + revs = unfi_cl.ancestors(headrevs, inclusive=True) + markers = repo.obsstore.relevantmarkers(revs=revs) markers = obsutil.sortedmarkers(markers) bundle2.buildobsmarkerspart(bundler, markers)
--- a/mercurial/obsolete.py Fri Sep 20 21:31:58 2024 -0400 +++ b/mercurial/obsolete.py Mon Jul 08 22:46:04 2024 +0200 @@ -773,10 +773,11 @@ _addchildren(self.children, markers) _checkinvalidmarkers(self.repo, markers) - def relevantmarkers(self, nodes): - """return a set of all obsolescence markers relevant to a set of nodes. + def relevantmarkers(self, nodes=None, revs=None): + """return a set of all obsolescence markers relevant to a set of + nodes or revisions. - "relevant" to a set of nodes mean: + "relevant" to a set of nodes or revisions mean: - marker that use this changeset as successor - prune marker of direct children on this changeset @@ -784,13 +785,33 @@ markers It is a set so you cannot rely on order.""" + if nodes is None: + nodes = set() + if revs is None: + revs = set() - pendingnodes = set(nodes) - seenmarkers = set() - seennodes = set(pendingnodes) + tonode = self.repo.unfiltered().changelog.node + pendingnodes = set() precursorsmarkers = self.predecessors succsmarkers = self.successors children = self.children + for node in nodes: + if ( + node in precursorsmarkers + or node in succsmarkers + or node in children + ): + pendingnodes.add(node) + for rev in revs: + node = tonode(rev) + if ( + node in precursorsmarkers + or node in succsmarkers + or node in children + ): + pendingnodes.add(node) + seenmarkers = set() + seennodes = pendingnodes.copy() while pendingnodes: direct = set() for current in pendingnodes:
--- a/mercurial/obsutil.py Fri Sep 20 21:31:58 2024 -0400 +++ b/mercurial/obsutil.py Mon Jul 08 22:46:04 2024 +0200 @@ -109,7 +109,7 @@ elif exclusive: rawmarkers = exclusivemarkers(repo, nodes) else: - rawmarkers = repo.obsstore.relevantmarkers(nodes) + rawmarkers = repo.obsstore.relevantmarkers(nodes=nodes) for markerdata in rawmarkers: yield marker(repo, markerdata)