From: "Yee Cheng Chin via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: Yee Cheng Chin <ychin.git@gmail.com>,
Yee Cheng Chin <ychin.git@gmail.com>
Subject: [PATCH v2] xdiff: optimize patience diff's LCS search
Date: Thu, 27 Nov 2025 02:16:06 +0000 [thread overview]
Message-ID: <pull.2109.v2.git.git.1764209766305.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.2109.git.git.1764152756908.gitgitgadget@gmail.com>
From: Yee Cheng Chin <ychin.git@gmail.com>
The find_longest_common_sequence() function in patience diff is
inefficient as it calls binary_search() for every unique line it
encounters when deciding where to put it in the sequence. From
instrumentation (using xctrace) on popular repositories, binary_search()
takes up 50-60% of the run time within patience_diff() when performing a
diff.
To optimize this, add a boundary condition check before binary_search()
is called to see if the encountered unique line is located after the
entire currently tracked longest subsequence. If so, skip the
unnecessary binary search and simply append the entry to the end of
sequence. Given that most files compared in a diff are usually quite
similar to each other, this condition is very common, and should be hit
much more frequently than the binary search.
Below are some end-to-end performance results by timing `git log
--shortstat --oneline -500 --patience` on different repositories with
the old and new code. Generally speaking this seems to give at least
8-10% speed up. The "binary search hit %" column describes how often the
algorithm enters the binary search path instead of the new faster path.
Even in the WebKit case we can see that it's quite rare (1.46%).
| Repo | Speed difference | binary search hit % |
|----------|------------------|---------------------|
| vim | 1.27x | 0.01% |
| pytorch | 1.16x | 0.02% |
| cpython | 1.14x | 0.06% |
| ripgrep | 1.14x | 0.03% |
| git | 1.13x | 0.12% |
| vscode | 1.09x | 0.10% |
| WebKit | 1.08x | 1.46% |
The benchmarks were done using hyperfine, on an Apple M1 Max laptop,
with git compiled with `-O3 -flto`.
Signed-off-by: Yee Cheng Chin <ychin.git@gmail.com>
---
xdiff: optimize patience diff's LCS search
Changes since v1:
* Fix typo in commit message for "pytortch"
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2109%2Fychin%2Fpatience-optimizations-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2109/ychin/patience-optimizations-v2
Pull-Request: https://github.com/git/git/pull/2109
Range-diff vs v1:
1: dc6d509b59 ! 1: 0f8a2dd719 xdiff: optimize patience diff's LCS search
@@ Commit message
| Repo | Speed difference | binary search hit % |
|----------|------------------|---------------------|
| vim | 1.27x | 0.01% |
- | pytortch | 1.16x | 0.02% |
+ | pytorch | 1.16x | 0.02% |
| cpython | 1.14x | 0.06% |
| ripgrep | 1.14x | 0.03% |
| git | 1.13x | 0.12% |
xdiff/xpatience.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/xdiff/xpatience.c b/xdiff/xpatience.c
index 669b653580..13ab0d591c 100644
--- a/xdiff/xpatience.c
+++ b/xdiff/xpatience.c
@@ -211,7 +211,10 @@ static int find_longest_common_sequence(struct hashmap *map, struct entry **res)
for (entry = map->first; entry; entry = entry->next) {
if (!entry->line2 || entry->line2 == NON_UNIQUE)
continue;
- i = binary_search(sequence, longest, entry);
+ if (longest == 0 || entry->line2 > sequence[longest - 1]->line2)
+ i = longest - 1;
+ else
+ i = binary_search(sequence, longest, entry);
entry->previous = i < 0 ? NULL : sequence[i];
++i;
if (i <= anchor_i)
base-commit: 6ab38b7e9cc7adafc304f3204616a4debd49c6e9
--
gitgitgadget
prev parent reply other threads:[~2025-11-27 2:16 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-11-26 10:25 [PATCH] xdiff: optimize patience diff's LCS search Yee Cheng Chin via GitGitGadget
2025-11-26 18:50 ` Junio C Hamano
2025-11-26 20:15 ` Yee Cheng Chin
2025-11-27 2:16 ` Yee Cheng Chin via GitGitGadget [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=pull.2109.v2.git.git.1764209766305.gitgitgadget@gmail.com \
--to=gitgitgadget@gmail.com \
--cc=git@vger.kernel.org \
--cc=ychin.git@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).