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 checkoutmaint
commit
8868ba1962
|
@ -604,12 +604,18 @@ static int switch_cache_bottom(struct traverse_info *info)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int are_same_oid(struct name_entry *name_j, struct name_entry *name_k)
|
||||||
|
{
|
||||||
|
return name_j->oid && name_k->oid && !oidcmp(name_j->oid, name_k->oid);
|
||||||
|
}
|
||||||
|
|
||||||
static int traverse_trees_recursive(int n, unsigned long dirmask,
|
static int traverse_trees_recursive(int n, unsigned long dirmask,
|
||||||
unsigned long df_conflicts,
|
unsigned long df_conflicts,
|
||||||
struct name_entry *names,
|
struct name_entry *names,
|
||||||
struct traverse_info *info)
|
struct traverse_info *info)
|
||||||
{
|
{
|
||||||
int i, ret, bottom;
|
int i, ret, bottom;
|
||||||
|
int nr_buf = 0;
|
||||||
struct tree_desc t[MAX_UNPACK_TREES];
|
struct tree_desc t[MAX_UNPACK_TREES];
|
||||||
void *buf[MAX_UNPACK_TREES];
|
void *buf[MAX_UNPACK_TREES];
|
||||||
struct traverse_info newinfo;
|
struct traverse_info newinfo;
|
||||||
|
@ -626,18 +632,40 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,
|
||||||
newinfo.pathlen += tree_entry_len(p) + 1;
|
newinfo.pathlen += tree_entry_len(p) + 1;
|
||||||
newinfo.df_conflicts |= df_conflicts;
|
newinfo.df_conflicts |= df_conflicts;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fetch the tree from the ODB for each peer directory in the
|
||||||
|
* n commits.
|
||||||
|
*
|
||||||
|
* For 2- and 3-way traversals, we try to avoid hitting the
|
||||||
|
* ODB twice for the same OID. This should yield a nice speed
|
||||||
|
* up in checkouts and merges when the commits are similar.
|
||||||
|
*
|
||||||
|
* We don't bother doing the full O(n^2) search for larger n,
|
||||||
|
* because wider traversals don't happen that often and we
|
||||||
|
* avoid the search setup.
|
||||||
|
*
|
||||||
|
* When 2 peer OIDs are the same, we just copy the tree
|
||||||
|
* descriptor data. This implicitly borrows the buffer
|
||||||
|
* data from the earlier cell.
|
||||||
|
*/
|
||||||
for (i = 0; i < n; i++, dirmask >>= 1) {
|
for (i = 0; i < n; i++, dirmask >>= 1) {
|
||||||
const unsigned char *sha1 = NULL;
|
if (i > 0 && are_same_oid(&names[i], &names[i - 1]))
|
||||||
if (dirmask & 1)
|
t[i] = t[i - 1];
|
||||||
sha1 = names[i].oid->hash;
|
else if (i > 1 && are_same_oid(&names[i], &names[i - 2]))
|
||||||
buf[i] = fill_tree_descriptor(t+i, sha1);
|
t[i] = t[i - 2];
|
||||||
|
else {
|
||||||
|
const unsigned char *sha1 = NULL;
|
||||||
|
if (dirmask & 1)
|
||||||
|
sha1 = names[i].oid->hash;
|
||||||
|
buf[nr_buf++] = fill_tree_descriptor(t+i, sha1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bottom = switch_cache_bottom(&newinfo);
|
bottom = switch_cache_bottom(&newinfo);
|
||||||
ret = traverse_trees(n, t, &newinfo);
|
ret = traverse_trees(n, t, &newinfo);
|
||||||
restore_cache_bottom(&newinfo, bottom);
|
restore_cache_bottom(&newinfo, bottom);
|
||||||
|
|
||||||
for (i = 0; i < n; i++)
|
for (i = 0; i < nr_buf; i++)
|
||||||
free(buf[i]);
|
free(buf[i]);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
Loading…
Reference in New Issue