Skip to content

Commit 8868ba1

Browse files
committed
Merge branch 'jh/unpack-trees-micro-optim'
In a 2- and 3-way merge of trees, more than one source trees often end up sharing an identical subtree; optimize by not reading the same tree multiple times in such a case. * jh/unpack-trees-micro-optim: unpack-trees: avoid duplicate ODB lookups during checkout
2 parents 8b6bba6 + d12a8cf commit 8868ba1

File tree

1 file changed

+33
-5
lines changed

1 file changed

+33
-5
lines changed

unpack-trees.c

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -604,12 +604,18 @@ static int switch_cache_bottom(struct traverse_info *info)
604604
return ret;
605605
}
606606

607+
static inline int are_same_oid(struct name_entry *name_j, struct name_entry *name_k)
608+
{
609+
return name_j->oid && name_k->oid && !oidcmp(name_j->oid, name_k->oid);
610+
}
611+
607612
static int traverse_trees_recursive(int n, unsigned long dirmask,
608613
unsigned long df_conflicts,
609614
struct name_entry *names,
610615
struct traverse_info *info)
611616
{
612617
int i, ret, bottom;
618+
int nr_buf = 0;
613619
struct tree_desc t[MAX_UNPACK_TREES];
614620
void *buf[MAX_UNPACK_TREES];
615621
struct traverse_info newinfo;
@@ -626,18 +632,40 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,
626632
newinfo.pathlen += tree_entry_len(p) + 1;
627633
newinfo.df_conflicts |= df_conflicts;
628634

635+
/*
636+
* Fetch the tree from the ODB for each peer directory in the
637+
* n commits.
638+
*
639+
* For 2- and 3-way traversals, we try to avoid hitting the
640+
* ODB twice for the same OID. This should yield a nice speed
641+
* up in checkouts and merges when the commits are similar.
642+
*
643+
* We don't bother doing the full O(n^2) search for larger n,
644+
* because wider traversals don't happen that often and we
645+
* avoid the search setup.
646+
*
647+
* When 2 peer OIDs are the same, we just copy the tree
648+
* descriptor data. This implicitly borrows the buffer
649+
* data from the earlier cell.
650+
*/
629651
for (i = 0; i < n; i++, dirmask >>= 1) {
630-
const unsigned char *sha1 = NULL;
631-
if (dirmask & 1)
632-
sha1 = names[i].oid->hash;
633-
buf[i] = fill_tree_descriptor(t+i, sha1);
652+
if (i > 0 && are_same_oid(&names[i], &names[i - 1]))
653+
t[i] = t[i - 1];
654+
else if (i > 1 && are_same_oid(&names[i], &names[i - 2]))
655+
t[i] = t[i - 2];
656+
else {
657+
const unsigned char *sha1 = NULL;
658+
if (dirmask & 1)
659+
sha1 = names[i].oid->hash;
660+
buf[nr_buf++] = fill_tree_descriptor(t+i, sha1);
661+
}
634662
}
635663

636664
bottom = switch_cache_bottom(&newinfo);
637665
ret = traverse_trees(n, t, &newinfo);
638666
restore_cache_bottom(&newinfo, bottom);
639667

640-
for (i = 0; i < n; i++)
668+
for (i = 0; i < nr_buf; i++)
641669
free(buf[i]);
642670

643671
return ret;

0 commit comments

Comments
 (0)