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

[PATCH 5/5] merge-ort: add prefetching for content merges

From
Elijah Newren via GitGitGadget <gitgitgadget@gmail.com>
Date
Jun 5, 2021, 01:28 UTC
Message-ID
<ca3b2a743b8e865e9e105620744f7a5aa16b24a4.1622856485.git.gitgitgadget@gmail.com>
In-Reply-To
<pull.969.git.1622856485.gitgitgadget@gmail.com>
From: Elijah Newren <newren@gmail.com>

Commit 7fbbcb21b1 ("diff: batch fetching of missing blobs", 2019-04-05) introduced batching of fetching missing blobs, so that the diff machinery would have one fetch subprocess grab N blobs instead of N processes each grabbing 1.

However, the diff machinery is not the only thing in a merge that needs to work on blobs. The 3-way content merges need them as well. Rather than download all the blobs 1 at a time, prefetch all the blobs needed for regular content merges.

This does not cover all possible paths in merge-ort that might need to
download blobs.  Others include:
  - The blob_unchanged() calls to avoid modify/delete conflicts (when
    blob renormalization results in an "unchanged" file)
  - Preliminary content merges needed for rename/add and
    rename/rename(2to1) style conflicts.  (Both of these types of
    conflicts can result in nested conflict markers from the need to do
    two levels of content merging; the first happens before our new
    prefetch_for_content_merges() function.)

The first of these wouldn't be an extreme amount of work to support, and even the second could be theoretically supported in batching, but all of these cases seem unusual to me, and this is a minor performance optimization anyway; in the worst case we only get some of the fetches batched and have a few additional one-off fetches. So for now, just handle the regular 3-way content merges in our prefetching.

For the testcase from the previous commit, the number of downloaded objects remains at 63, but this drops the number of fetches needed from 32 down to 20, a sizeable reduction.

Signed-off-by: Elijah Newren <newren@gmail.com>
---
 merge-ort.c                    | 50 ++++++++++++++++++++++++++++++++++
 t/t6421-merge-partial-clone.sh |  2 +-
 2 files changed, 51 insertions(+), 1 deletion(-)
diff --git a/merge-ort.c b/merge-ort.c
index cfa751053b01..e3a5dfc7b312 100644
--- a/merge-ort.c
+++ b/merge-ort.c
@@ -29,6 +29,7 @@
 #include "entry.h"
 #include "ll-merge.h"
 #include "object-store.h"
+#include "promisor-remote.h"
 #include "revision.h"
 #include "strmap.h"
 #include "submodule.h"
@@ -3485,6 +3486,54 @@ static void process_entry(struct merge_options *opt,
 	record_entry_for_tree(dir_metadata, path, &ci->merged);
 }
 
+static void prefetch_for_content_merges(struct merge_options *opt,
+					struct string_list *plist)
+{
+	struct string_list_item *e;
+	struct oid_array to_fetch = OID_ARRAY_INIT;
+
+	if (opt->repo != the_repository || !has_promisor_remote())
+		return;
+
+	for (e = &plist->items[plist->nr-1]; e >= plist->items; --e) {
+		/* char *path = e->string; */
+		struct conflict_info *ci = e->util;
+		int i;
+
+		/* Ignore clean entries */
+		if (ci->merged.clean)
+			continue;
+
+		/* Ignore entries that don't need a content merge */
+		if (ci->match_mask || ci->filemask < 6 ||
+		    !S_ISREG(ci->stages[1].mode) ||
+		    !S_ISREG(ci->stages[2].mode) ||
+		    oideq(&ci->stages[1].oid, &ci->stages[2].oid))
+			continue;
+
+		/* Also don't need content merge if base matches either side */
+		if (ci->filemask == 7 &&
+		    S_ISREG(ci->stages[0].mode) &&
+		    (oideq(&ci->stages[0].oid, &ci->stages[1].oid) ||
+		     oideq(&ci->stages[0].oid, &ci->stages[2].oid)))
+			continue;
+
+		for (i = 0; i < 3; i++) {
+			unsigned side_mask = (1 << i);
+			struct version_info *vi = &ci->stages[i];
+
+			if ((ci->filemask & side_mask) &&
+			    S_ISREG(vi->mode) &&
+			    oid_object_info_extended(opt->repo, &vi->oid, NULL,
+						     OBJECT_INFO_FOR_PREFETCH))
+				oid_array_append(&to_fetch, &vi->oid);
+		}
+	}
+
+	promisor_remote_get_direct(opt->repo, to_fetch.oid, to_fetch.nr);
+	oid_array_clear(&to_fetch);
+}
+
 static void process_entries(struct merge_options *opt,
 			    struct object_id *result_oid)
 {
@@ -3531,6 +3580,7 @@ static void process_entries(struct merge_options *opt,
 	 * the way when it is time to process the file at the same path).
 	 */
 	trace2_region_enter("merge", "processing", opt->repo);
+	prefetch_for_content_merges(opt, &plist);
 	for (entry = &plist.items[plist.nr-1]; entry >= plist.items; --entry) {
 		char *path = entry->string;
 		/*
diff --git a/t/t6421-merge-partial-clone.sh b/t/t6421-merge-partial-clone.sh
index fb7eb18cc80c..84642cb3831c 100755
--- a/t/t6421-merge-partial-clone.sh
+++ b/t/t6421-merge-partial-clone.sh
@@ -392,7 +392,7 @@ test_expect_merge_algorithm failure success 'Objects downloaded when a directory
 #
 #   Summary: 4 fetches (1 for 6 objects, 1 for 8, 1 for 3, 1 for 2)
 #
-test_expect_merge_algorithm failure failure 'Objects downloaded with lots of renames and modifications' '
+test_expect_merge_algorithm failure success 'Objects downloaded with lots of renames and modifications' '
 	test_setup_repo &&
 	git clone --sparse --filter=blob:none "file://$(pwd)/server" objects-many &&
 	(
-- 
gitgitgadget
Previous: Derrick StoleeNext: Elijah Newren via GitGitGadget
Message 4 of 30 in “Optimization batch 13: partial clone optimizations for merge-ort”
  1. 0/5 Optimization batch 13: partial clone optimizations for merge-ortElijah Newren via GitGitGadget, Jun 5, 2021
  2. 1/5 promisor-remote: output trace2 statistics for number of objects fetchedElijah Newren via GitGitGadget, Jun 5, 2021
  3. Derrick StoleeJun 9, 2021
  4. 5/5 merge-ort: add prefetching for content mergesElijah Newren via GitGitGadget, Jun 5, 2021
  5. 3/5 diffcore-rename: allow different missing_object_cb functionsElijah Newren via GitGitGadget, Jun 5, 2021
  6. 2/5 t6421: add tests checking for excessive object downloads during mergeElijah Newren via GitGitGadget, Jun 5, 2021
  7. Derrick StoleeJun 9, 2021
  8. 4/5 diffcore-rename: use a different prefetch for basename comparisonsElijah Newren via GitGitGadget, Jun 5, 2021
  9. 0/5 Optimization batch 13: partial clone optimizations for merge-ortElijah Newren via GitGitGadget, Jun 15, 2021
  10. 1/5 promisor-remote: output trace2 statistics for number of objects fetchedElijah Newren via GitGitGadget, Jun 15, 2021
  11. 2/5 t6421: add tests checking for excessive object downloads during mergeElijah Newren via GitGitGadget, Jun 15, 2021
  12. Junio C HamanoJun 17, 2021
  13. 4/5 diffcore-rename: use a different prefetch for basename comparisonsElijah Newren via GitGitGadget, Jun 15, 2021
  14. 3/5 diffcore-rename: allow different missing_object_cb functionsElijah Newren via GitGitGadget, Jun 15, 2021
  15. 5/5 merge-ort: add prefetching for content mergesElijah Newren via GitGitGadget, Jun 15, 2021
  16. Junio C HamanoJun 17, 2021
  17. Elijah NewrenJun 22, 2021
  18. Derrick StoleeJun 16, 2021
  19. Junio C HamanoJun 17, 2021
  20. 0/5 Optimization batch 13: partial clone optimizations for merge-ortElijah Newren via GitGitGadget, Jun 22, 2021
  21. 1/5 promisor-remote: output trace2 statistics for number of objects fetchedElijah Newren via GitGitGadget, Jun 22, 2021
  22. 3/5 diffcore-rename: allow different missing_object_cb functionsElijah Newren via GitGitGadget, Jun 22, 2021
  23. 2/5 t6421: add tests checking for excessive object downloads during mergeElijah Newren via GitGitGadget, Jun 22, 2021
  24. 4/5 diffcore-rename: use a different prefetch for basename comparisonsElijah Newren via GitGitGadget, Jun 22, 2021
  25. 5/5 merge-ort: add prefetching for content mergesElijah Newren via GitGitGadget, Jun 22, 2021
  26. Derrick StoleeJun 22, 2021
  27. Elijah NewrenJun 22, 2021
  28. Derrick StoleeJun 23, 2021
  29. Elijah NewrenJun 23, 2021
  30. Elijah NewrenJun 23, 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.