unpack-trees: improve performance of next_cache_entry

To find the first non-unpacked cache entry, `next_cache_entry` iterates through index, starting at `cache_bottom`. The performance of this in full indexes is helped by `cache_bottom` advancing with each invocation of `mark_ce_used` (called by `unpack_index_entry`). However, the presence of sparse directories can prevent the `cache_bottom` from advancing in a sparse index case, effectively forcing `next_cache_entry` to search from the beginning of the index each time it is called. The `cache_bottom` must be preserved for the sparse index (see 17a1bb570b (unpack-trees: preserve cache_bottom, 2021-07-14)). Therefore, to retain the benefit `cache_bottom` provides in non-sparse index cases, a separate `hint` position indicates the first position `next_cache_entry` should search, updated each execution with a new position. Signed-off-by: Victoria Dye <vdye@github.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Victoria Dye committed Nov 29, 2021 at 15:52 UTC f2a454e0a5e26c0f7b840970f69d195c37b16565
1 file changed +17 -6
unpack-trees.c
+17 -6
@@ -645,17 +645,24 @@ static void mark_ce_used_same_name(struct cache_entry *ce,
645 }
646 }
647
648 -static struct cache_entry *next_cache_entry(struct unpack_trees_options *o)
648 +static struct cache_entry *next_cache_entry(struct unpack_trees_options *o, int *hint)
649 {
650 const struct index_state *index = o->src_index;
651 int pos = o->cache_bottom;
652
653 + if (*hint > pos)
654 + pos = *hint;
655 +
656 while (pos < index->cache_nr) {
657 struct cache_entry *ce = index->cache[pos];
655 - if (!(ce->ce_flags & CE_UNPACKED))
658 + if (!(ce->ce_flags & CE_UNPACKED)) {
659 + *hint = pos + 1;
660 return ce;
661 + }
662 pos++;
663 }
664 +
665 + *hint = pos;
666 return NULL;
667 }
668
@@ -1365,12 +1372,13 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str
1372
1373 /* Are we supposed to look at the index too? */
1374 if (o->merge) {
1375 + int hint = -1;
1376 while (1) {
1377 int cmp;
1378 struct cache_entry *ce;
1379
1380 if (o->diff_index_cached)
1373 - ce = next_cache_entry(o);
1381 + ce = next_cache_entry(o, &hint);
1382 else
1383 ce = find_cache_entry(info, p);
1384
@@ -1690,7 +1698,7 @@ static int verify_absent(const struct cache_entry *,
1698 int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options *o)
1699 {
1700 struct repository *repo = the_repository;
1693 - int i, ret;
1701 + int i, hint, ret;
1702 static struct cache_entry *dfc;
1703 struct pattern_list pl;
1704 int free_pattern_list = 0;
@@ -1763,13 +1771,15 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
1771 info.pathspec = o->pathspec;
1772
1773 if (o->prefix) {
1774 + hint = -1;
1775 +
1776 /*
1777 * Unpack existing index entries that sort before the
1778 * prefix the tree is spliced into. Note that o->merge
1779 * is always true in this case.
1780 */
1781 while (1) {
1772 - struct cache_entry *ce = next_cache_entry(o);
1782 + struct cache_entry *ce = next_cache_entry(o, &hint);
1783 if (!ce)
1784 break;
1785 if (ce_in_traverse_path(ce, &info))
@@ -1790,8 +1800,9 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
1800
1801 /* Any left-over entries in the index? */
1802 if (o->merge) {
1803 + hint = -1;
1804 while (1) {
1794 - struct cache_entry *ce = next_cache_entry(o);
1805 + struct cache_entry *ce = next_cache_entry(o, &hint);
1806 if (!ce)
1807 break;
1808 if (unpack_index_entry(ce, o) < 0)