git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH 0/7] Optimization batch 14: trivial directory resolution

From
Elijah Newren <newren@gmail.com>
Date
Jul 1, 2021, 15:04 UTC
Message-ID
<CABPp-BGRgL-SnHDgefq887e51z9t_TMXcc4DLrazggC-1iQWng@mail.gmail.com>
In-Reply-To
<87wnqacg9i.fsf@evledraar.gmail.com>
On Thu, Jul 1, 2021 at 6:31 AM Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:
Show 20 quoted lines
>
> On Thu, Jul 01 2021, Elijah Newren via GitGitGadget wrote:
>
> > This series depends textually on ort-perf-batch-12, but is semantically
> > independent. (It is both semantically and textually independent of
> > ort-perf-batch-13.)
>
> For others following along, that ort-perf-batch-12 is at
> https://lore.kernel.org/git/pull.962.v4.git.1623168703.gitgitgadget@gmail.com/#t
> & currently marked as 'will merge to next' in what's cooking.
>
> > Most of my previous series dramatically accelerated cases with lots of
> > renames, while providing comparatively minor benefits for cases with few or
> > no renames. This series is the opposite; it provides huge benefits when
> > there are few or no renames, and comparatively smaller (though still quite
> > decent) benefits for cases with many uncached renames.
>
> Sounds good, one thing I haven't seen at a glance is how these
> performance numbers compare to the merge-recursive backend. Are we in a
> state of reaching parity with it, or pulling ahead?

Oh, wow, I really need to improve my wording if that's a question. When I referred to "the performance results we started with" at the end, that's referring to the numbers I got with merge-recursive before I started this journey. So these were the merge-recursive numbers (with git-2.29.0 or git-2.30.0):

no-renames-am: 6.940 s ± 0.485 s no-renames: 18.912 s ± 0.174 s mega-renames: 5964.031 s ± 10.459 s just-one-mega: 149.583 s ± 0.751 s

As per commit 557ac0350d ("merge-ort: begin performance work; instrument with trace2_region_* calls", 2021-01-23), merge-ort was faster than merge-recursive at the beginning. But I wasn't content with that....

Also in the cover letter, I showed the merge-ort numbers before and after this series:

no-renames: 5.235 s ± 0.042 s 204.2 ms ± 3.0 ms mega-renames: 9.419 s ± 0.107 s 1.076 s ± 0.015 s just-one-mega: 480.1 ms ± 3.9 ms 364.1 ms ± 7.0 ms

So yeah, we've pulled a little ahead.  (Like my understatement there?)

Granted, merge-recursive doesn't take quite as long as it used to; it also benefited from some of my optimizations[1]. Nowhere near as much as merge-ort benefited, but it still would be about 20x faster on the cases with "mega" in their name. So, although today's merge-ort is ~5542.7x faster than git-2.29.0's merge-recursive for a massive set of renames in a really long rebase sequence, it is probably only a mere 277x faster than today's merge-recursive on the same case.

If you'd like to catch up on the various optimizations, you can find most of them with this:

git log --reverse --grep=mega-renames --author=newren

[1] In particular, merge-recursive would benefit from these ones: 12bd17521c ("Merge branch 'en/diffcore-rename'", 2021-03-01), d3a035b055 ("Merge branch 'en/merge-ort-perf'", 2021-02-11), and a5ac31b5b1 ("Merge branch 'en/diffcore-rename'", 2021-01-25)

Show 23 quoted lines
> > [...]
> > For the testcases mentioned in commit 557ac0350d ("merge-ort: begin
> > performance work; instrument with trace2_region_* calls", 2020-10-28), the
> > changes in just this series improves the performance as follows:
> >
> >                      Before Series           After Series
> > no-renames:        5.235 s ±  0.042 s   204.2  ms ±  3.0  ms
> > mega-renames:      9.419 s ±  0.107 s     1.076 s ±  0.015 s
> > just-one-mega:   480.1  ms ±  3.9  ms   364.1  ms ±  7.0  ms
> >
> >
> > As a reminder, before any merge-ort/diffcore-rename performance work, the
> > performance results we started with were:
> >
> > no-renames-am:      6.940 s ±  0.485 s
> > no-renames:        18.912 s ±  0.174 s
> > mega-renames:    5964.031 s ± 10.459 s
> > just-one-mega:    149.583 s ±  0.751 s
>
> I haven't given any of this a detailed look, just a note/question that
> (depending on the answer to the "v.s. merge-recursive above") we may
> want to consider bumping the default for the diff.renamelimit at some
> point along with any major optimizations.

I do think we should bump the diff.renamelimit, but not for these reasons. We should bump them for the same reasons we bumped them last time at commit 92c57e5c1d ("bump rename limit defaults (again)", 2011-02-19).

In particular, none of my optimizations made the quadratic portion of rename detection any faster. It just added multiple ways for things to either skip rename detection or be handled by a linear algorithm instead and not be included in the quadratic loop. Since diff.renamelimit only applies to the stuff that goes into the quadratic portion, most folks will notice that newer versions of git detect renames with the same limits that older git would have given up with.

It perhaps also is worth bearing that some of my rename detection optimizations were specific to three-way merges (and thus help merge/cherry-pick/rebase/etc. but not diff/log), some of my optimizations were specific to reapplying long sequences of linear commits (and thus help cherry-pick or rebase but not diff, log, or even merge), while others help out all uses of the diff machinery (diff/log/merge/cherry-pick/rebase/revert/etc.).

Show 27 quoted lines
> <random musings follow, the tl;dr is above this line :)>
>
> As an aside that we have diff.renamelimit is one of the most "dangerous"
> landmines/fork-in-eye/shotgun-to-foot edge cases we have in using diff
> as plumbing IMO.
>
> E.g. I somewhat recently had to deal with some 3rd party Go-language
> lint plugin that can be configured to enforce lints "as of a commit".
> I.e. it does a diff from that commit, sees in any introduced "issues"
> are "new", and complains accordingly. The idea is that it allows you to
> enforce lints on "only new code", say ignoring the return value of
> os.Write(), without insisting that all existing code must be
> whitelisted/fixed first.
>
> The problem being two-fold, one that the thing will get slower over time
> as we grow history (can't be avoided), but the more subtle one that at
> some point we'll bump into the diff.renamelimit, and whatever unlucky
> sob does so will find that the lint is now complaining about ALL THE
> THINGS, since "old" code is now ending up as "new" to a naïve diff
> parser relying on not bumping into the diff.renamelimit.
>
> Arguably bumping the diff.renamelimit would make that sort of problem
> worse for plumbing consumers, since they'd have more rope with which to
> hang themselves, maybe it's better to step on that landmine early.
>
> Sorry about the digression somewhat pointless but perhaps amusing
> digression in the last 4 paragraphs :)
Certainly a digression, but yes it's amusing.  :-)

Let me add a digression of my own: I got started on this because people complained they couldn't cherry-pick patches to maintenance branches, git just messed it all up when it gave up on renames. I told them to set diff.renameLimit higher, and they told me that didn't work.

I didn't believe them.

So, I started on a bit of a journey, somewhere around commit 9f7e4bfa3b ("diff: remove silent clamp of renameLimit", 2017-11-13). Then after that commit, git could detect renames but took forever doing so (we needed a renameLimit of 48941 for the very first testcase I was pointed at). I dug into that, trying to figure out why cherry-picking a simple patch that modified 7 files (and not with particularly big edits) would take something approaching 10 minutes to complete. Then I went and found optimizations and rewrote the entire merge machinery. Now that same cherry-pick can complete without bumping diff.renameLimit (1000 is more than enough), and completes in less than a second (well, with all my optimizations, including the series not yet submitted).

So, as I said, I didn't believe them when they reported git couldn't detect renames for their case. Look at all the work I've done in order to continue to not believe them. ;-)

Previous: Ævar Arnfjörð BjarmasonNext: Elijah Newren
Message 10 of 52 in “Optimization batch 14: trivial directory resolution”
  1. 0/7 Optimization batch 14: trivial directory resolutionElijah Newren via GitGitGadget, Jul 1, 2021
  2. 2/7 merge-ort: add some more explanations in collect_merge_info_callback()Elijah Newren via GitGitGadget, Jul 1, 2021
  3. 1/7 merge-ort: resolve paths early when we have sufficient informationElijah Newren via GitGitGadget, Jul 1, 2021
  4. 5/7 merge-ort: defer recursing into directories when merge base is matchedElijah Newren via GitGitGadget, Jul 1, 2021
  5. 4/7 merge-ort: add a handle_deferred_entries() helper functionElijah Newren via GitGitGadget, Jul 1, 2021
  6. 3/7 merge-ort: add data structures for allowable trivial directory resolvesElijah Newren via GitGitGadget, Jul 1, 2021
  7. 6/7 merge-ort: avoid recursing into directories when we don't need toElijah Newren via GitGitGadget, Jul 1, 2021
  8. 7/7 merge-ort: restart merge with cached renames to reduce process entry costElijah Newren via GitGitGadget, Jul 1, 2021
  9. Ævar Arnfjörð BjarmasonJul 1, 2021
  10. Elijah NewrenJul 1, 2021
  11. Elijah NewrenJul 1, 2021
  12. 0/7 Optimization batch 14: trivial directory resolutionElijah Newren via GitGitGadget, Jul 13, 2021
  13. 1/7 merge-ort: resolve paths early when we have sufficient informationElijah Newren via GitGitGadget, Jul 13, 2021
  14. 2/7 merge-ort: add some more explanations in collect_merge_info_callback()Elijah Newren via GitGitGadget, Jul 13, 2021
  15. Bagas SanjayaJul 13, 2021
  16. Elijah NewrenJul 14, 2021
  17. 3/7 merge-ort: add data structures for allowable trivial directory resolvesElijah Newren via GitGitGadget, Jul 13, 2021
  18. Derrick StoleeJul 15, 2021
  19. Elijah NewrenJul 15, 2021
  20. 7/7 merge-ort: restart merge with cached renames to reduce process entry costElijah Newren via GitGitGadget, Jul 13, 2021
  21. Derrick StoleeJul 15, 2021
  22. Elijah NewrenJul 15, 2021
  23. Derrick StoleeJul 15, 2021
  24. Elijah NewrenJul 15, 2021
  25. 4/7 merge-ort: add a handle_deferred_entries() helper functionElijah Newren via GitGitGadget, Jul 13, 2021
  26. Derrick StoleeJul 15, 2021
  27. Elijah NewrenJul 15, 2021
  28. 5/7 merge-ort: defer recursing into directories when merge base is matchedElijah Newren via GitGitGadget, Jul 13, 2021
  29. Derrick StoleeJul 15, 2021
  30. Elijah NewrenJul 15, 2021
  31. Derrick StoleeJul 15, 2021
  32. 6/7 merge-ort: avoid recursing into directories when we don't need toElijah Newren via GitGitGadget, Jul 13, 2021
  33. Derrick StoleeJul 15, 2021
  34. Elijah NewrenJul 15, 2021
  35. 0/7 Optimization batch 14: trivial directory resolutionElijah Newren via GitGitGadget, Jul 16, 2021
  36. 1/7 merge-ort: resolve paths early when we have sufficient informationElijah Newren via GitGitGadget, Jul 16, 2021
  37. 2/7 merge-ort: add some more explanations in collect_merge_info_callback()Elijah Newren via GitGitGadget, Jul 16, 2021
  38. 4/7 merge-ort: add a handle_deferred_entries() helper functionElijah Newren via GitGitGadget, Jul 16, 2021
  39. 3/7 merge-ort: add data structures for allowable trivial directory resolvesElijah Newren via GitGitGadget, Jul 16, 2021
  40. 5/7 merge-ort: defer recursing into directories when merge base is matchedElijah Newren via GitGitGadget, Jul 16, 2021
  41. 6/7 merge-ort: avoid recursing into directories when we don't need toElijah Newren via GitGitGadget, Jul 16, 2021
  42. 7/7 merge-ort: restart merge with cached renames to reduce process entry costElijah Newren via GitGitGadget, Jul 16, 2021
  43. Derrick StoleeJul 20, 2021
  44. Junio C HamanoJul 20, 2021
  45. 0/7 Optimization batch 14: trivial directory resolutionElijah Newren via GitGitGadget, Jul 21, 2021
  46. 1/7 merge-ort: resolve paths early when we have sufficient informationElijah Newren via GitGitGadget, Jul 21, 2021
  47. 2/7 merge-ort: add some more explanations in collect_merge_info_callback()Elijah Newren via GitGitGadget, Jul 21, 2021
  48. 3/7 merge-ort: add data structures for allowable trivial directory resolvesElijah Newren via GitGitGadget, Jul 21, 2021
  49. 4/7 merge-ort: add a handle_deferred_entries() helper functionElijah Newren via GitGitGadget, Jul 21, 2021
  50. 5/7 merge-ort: defer recursing into directories when merge base is matchedElijah Newren via GitGitGadget, Jul 21, 2021
  51. 6/7 merge-ort: avoid recursing into directories when we don't need toElijah Newren via GitGitGadget, Jul 21, 2021
  52. 7/7 merge-ort: restart merge with cached renames to reduce process entry costElijah Newren via GitGitGadget, Jul 21, 2021

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.