# HG changeset patch # User Pierre-Yves David # Date 1725354677 -7200 # Node ID 42a116f1cdc152f75aac49c52660a2fb6d3ecaee # Parent 41b8892a205411c6b2ae14be857fb41fd8cf43f2 branchmap-v3: introduce a "stop_rev" argument to `headsrevs` The `headsrevs` method of the revlog already have a `revs` argument to compute the headrevs of a limited set of heads. However, it disable the use of the native compiled code to compute the heads, which slows down the branchmap v3 code a lot. The branchmap v3 usage is actually quite constrained as we will always only ignores a part at the top of the graph. So we could be significantly faster. We start by making small change to the python side to improve the situation and introduce the new API. More collaboration with the native code are coming later. This massively speedup operation and close most of the remaining gaps between branchmap-v3 and branchmap-v2. especially on repository with many revs like mozilla-try. A small overhead remains mostly because the `headrevs` logic currently has some inefficiently. We will look into them from there. ### benchmark.name = hg.command.unbundle # bin-env-vars.hg.py-re2-module = default # benchmark.variants.issue6528 = disabled # benchmark.variants.resource-usage = default # benchmark.variants.reuse-external-delta-parent = yes # benchmark.variants.revs = any-1-extra-rev # benchmark.variants.source = unbundle # benchmark.variants.validate = default # benchmark.variants.verbosity = quiet ## data-env-vars.name = netbeans-2018-08-01-zstd-sparse-revlog # bin-env-vars.hg.flavor = default branch-v2: 0.233711 ~~~~~ branch-v3 before: 0.368769 (+57.79%, +0.14) branch-v3 after: 0.239857 (+2.63%, +0.01) # bin-env-vars.hg.flavor = rust branch-v2: 0.235230 ~~~~~ branch-v3 before: 0.372460 (+58.34%, +0.14) branch-v3 after: 0.240972 (+2.44%, +0.01) ## data-env-vars.name = netbeans-2018-08-01-ds2-pnm # bin-env-vars.hg.flavor = rust branch-v2: 0.255586 ~~~~~ branch-v3 before: 0.318907 (+24.78%, +0.06) branch-v3 after: 0.268560 (+5.08%, +0.01) ## data-env-vars.name = mozilla-central-2024-03-22-zstd-sparse-revlog # bin-env-vars.hg.flavor = default branch-v2: 0.339010 ~~~~~ branch-v3 before: 0.349752 (+3.17%, +0.01) branch-v3 after: 0.349389 (+3.06%, +0.01) # bin-env-vars.hg.flavor = rust branch-v2: 0.346525 ~~~~~ branch-v3 before: 0.354300 (+2.24%, +0.01) branch-v3 after: 0.355661 (+2.64%, +0.01) ## data-env-vars.name = mozilla-central-2024-03-22-ds2-pnm # bin-env-vars.hg.flavor = rust branch-v2: 0.380202 ~~~~~ branch-v3 before: 0.396293 (+4.23%, +0.02) branch-v3 after: 0.408851 (+7.54%, +0.03) ## data-env-vars.name = mozilla-unified-2024-03-22-zstd-sparse-revlog # bin-env-vars.hg.flavor = default branch-v2: 0.412165 ~~~~~ branch-v3 before: 0.424769 (+3.06%, +0.01) branch-v3 after: 0.427782 (+3.79%, +0.02) # bin-env-vars.hg.flavor = rust branch-v2: 0.412397 ~~~~~ branch-v3 before: 0.421796 (+2.28%, +0.01) branch-v3 after: 0.422354 (+2.41%, +0.01) ## data-env-vars.name = mozilla-unified-2024-03-22-ds2-pnm # bin-env-vars.hg.flavor = rust branch-v2: 0.429501 ~~~~~ branch-v3 before: 0.443849 (+3.34%, +0.01) branch-v3 after: 0.443197 (+3.19%, +0.01) ## data-env-vars.name = mozilla-try-2024-03-26-zstd-sparse-revlog # bin-env-vars.hg.flavor = default branch-v2: 3.403171 ~~~~~ branch-v3 before: 6.234055 (+83.18%, +2.83) branch-v3 after: 3.819477 (+12.23%, +0.42) # bin-env-vars.hg.flavor = rust branch-v2: 3.454876 ~~~~~ branch-v3 before: 6.307813 (+82.58%, +2.85) branch-v3 after: 3.590284 (+3.92%, +0.14) ## data-env-vars.name = mozilla-try-2024-03-26-ds2-pnm # bin-env-vars.hg.flavor = rust branch-v2: 3.465435 ~~~~~ branch-v3 before: 5.176076 (+49.36%, +1.71) branch-v3 after: 3.633278 (+4.84%, +0.17) diff -r 41b8892a2054 -r 42a116f1cdc1 mercurial/branchmap.py --- a/mercurial/branchmap.py Tue Sep 03 02:13:03 2024 +0200 +++ b/mercurial/branchmap.py Tue Sep 03 11:11:17 2024 +0200 @@ -884,8 +884,7 @@ elif self.tiprev == cl.tiprev(): return cl.headrevs() else: - # XXX passing tiprev as ceiling of cl.headrevs could be faster - heads = cl.headrevs(cl.revs(stop=self.tiprev)) + heads = cl.headrevs(stop_rev=self.tiprev + 1) return heads def _write_header(self, fp) -> None: diff -r 41b8892a2054 -r 42a116f1cdc1 mercurial/repoview.py --- a/mercurial/repoview.py Tue Sep 03 02:13:03 2024 +0200 +++ b/mercurial/repoview.py Tue Sep 03 11:11:17 2024 +0200 @@ -310,9 +310,14 @@ # no Rust fast path implemented yet, so just loop in Python return [self.node(r) for r in self.headrevs()] - def headrevs(self, revs=None): + def headrevs(self, revs=None, stop_rev=None): if revs is None: - return self.index.headrevs(self.filteredrevs) + filtered = self.filteredrevs + if stop_rev is not None and stop_rev < len(self.index): + filtered = set(self.filteredrevs) + filtered.update(range(stop_rev, len(self.index))) + return self.index.headrevs(filtered) + assert stop_rev is None revs = self._checknofilteredinrevs(revs) return super(filteredchangelogmixin, self).headrevs(revs) diff -r 41b8892a2054 -r 42a116f1cdc1 mercurial/revlog.py --- a/mercurial/revlog.py Tue Sep 03 02:13:03 2024 +0200 +++ b/mercurial/revlog.py Tue Sep 03 11:11:17 2024 +0200 @@ -2380,9 +2380,15 @@ assert heads return (orderedout, roots, heads) - def headrevs(self, revs=None): + def headrevs(self, revs=None, stop_rev=None): if revs is None: - return self.index.headrevs() + excluded = None + if stop_rev is not None and stop_rev < len(self.index): + # We should let the native code handle it, but that a + # simple enough first step. + excluded = range(stop_rev, len(self.index)) + return self.index.headrevs(excluded) + assert stop_rev is None if rustdagop is not None and self.index.rust_ext_compat: return rustdagop.headrevs(self.index, revs) return dagop.headrevs(revs, self._uncheckedparentrevs)