Merge branch 'dt/unpack-compare-entry-optim'
"git checkout $branch" (and other operations that share the same underlying machinery) has been optimized. * dt/unpack-compare-entry-optim: unpack-trees: fix accidentally quadratic behavior do_compare_entry: use already-computed path
This commit is contained in:
@ -320,6 +320,7 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
|
|||||||
struct tree_desc_x *tx = xcalloc(n, sizeof(*tx));
|
struct tree_desc_x *tx = xcalloc(n, sizeof(*tx));
|
||||||
struct strbuf base = STRBUF_INIT;
|
struct strbuf base = STRBUF_INIT;
|
||||||
int interesting = 1;
|
int interesting = 1;
|
||||||
|
char *traverse_path;
|
||||||
|
|
||||||
for (i = 0; i < n; i++)
|
for (i = 0; i < n; i++)
|
||||||
tx[i].d = t[i];
|
tx[i].d = t[i];
|
||||||
@ -329,7 +330,11 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
|
|||||||
make_traverse_path(base.buf, info->prev, &info->name);
|
make_traverse_path(base.buf, info->prev, &info->name);
|
||||||
base.buf[info->pathlen-1] = '/';
|
base.buf[info->pathlen-1] = '/';
|
||||||
strbuf_setlen(&base, info->pathlen);
|
strbuf_setlen(&base, info->pathlen);
|
||||||
|
traverse_path = xstrndup(base.buf, info->pathlen);
|
||||||
|
} else {
|
||||||
|
traverse_path = xstrndup(info->name.path, info->pathlen);
|
||||||
}
|
}
|
||||||
|
info->traverse_path = traverse_path;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
int trees_used;
|
int trees_used;
|
||||||
unsigned long mask, dirmask;
|
unsigned long mask, dirmask;
|
||||||
@ -411,6 +416,8 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
|
|||||||
for (i = 0; i < n; i++)
|
for (i = 0; i < n; i++)
|
||||||
free_extended_entry(tx + i);
|
free_extended_entry(tx + i);
|
||||||
free(tx);
|
free(tx);
|
||||||
|
free(traverse_path);
|
||||||
|
info->traverse_path = NULL;
|
||||||
strbuf_release(&base);
|
strbuf_release(&base);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
@ -59,6 +59,7 @@ enum follow_symlinks_result {
|
|||||||
enum follow_symlinks_result get_tree_entry_follow_symlinks(unsigned char *tree_sha1, const char *name, unsigned char *result, struct strbuf *result_path, unsigned *mode);
|
enum follow_symlinks_result get_tree_entry_follow_symlinks(unsigned char *tree_sha1, const char *name, unsigned char *result, struct strbuf *result_path, unsigned *mode);
|
||||||
|
|
||||||
struct traverse_info {
|
struct traverse_info {
|
||||||
|
const char *traverse_path;
|
||||||
struct traverse_info *prev;
|
struct traverse_info *prev;
|
||||||
struct name_entry name;
|
struct name_entry name;
|
||||||
int pathlen;
|
int pathlen;
|
||||||
|
@ -498,13 +498,14 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,
|
|||||||
* itself - the caller needs to do the final check for the cache
|
* itself - the caller needs to do the final check for the cache
|
||||||
* entry having more data at the end!
|
* entry having more data at the end!
|
||||||
*/
|
*/
|
||||||
static int do_compare_entry(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n)
|
static int do_compare_entry_piecewise(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n)
|
||||||
{
|
{
|
||||||
int len, pathlen, ce_len;
|
int len, pathlen, ce_len;
|
||||||
const char *ce_name;
|
const char *ce_name;
|
||||||
|
|
||||||
if (info->prev) {
|
if (info->prev) {
|
||||||
int cmp = do_compare_entry(ce, info->prev, &info->name);
|
int cmp = do_compare_entry_piecewise(ce, info->prev,
|
||||||
|
&info->name);
|
||||||
if (cmp)
|
if (cmp)
|
||||||
return cmp;
|
return cmp;
|
||||||
}
|
}
|
||||||
@ -522,6 +523,39 @@ static int do_compare_entry(const struct cache_entry *ce, const struct traverse_
|
|||||||
return df_name_compare(ce_name, ce_len, S_IFREG, n->path, len, n->mode);
|
return df_name_compare(ce_name, ce_len, S_IFREG, n->path, len, n->mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int do_compare_entry(const struct cache_entry *ce,
|
||||||
|
const struct traverse_info *info,
|
||||||
|
const struct name_entry *n)
|
||||||
|
{
|
||||||
|
int len, pathlen, ce_len;
|
||||||
|
const char *ce_name;
|
||||||
|
int cmp;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we have not precomputed the traverse path, it is quicker
|
||||||
|
* to avoid doing so. But if we have precomputed it,
|
||||||
|
* it is quicker to use the precomputed version.
|
||||||
|
*/
|
||||||
|
if (!info->traverse_path)
|
||||||
|
return do_compare_entry_piecewise(ce, info, n);
|
||||||
|
|
||||||
|
cmp = strncmp(ce->name, info->traverse_path, info->pathlen);
|
||||||
|
if (cmp)
|
||||||
|
return cmp;
|
||||||
|
|
||||||
|
pathlen = info->pathlen;
|
||||||
|
ce_len = ce_namelen(ce);
|
||||||
|
|
||||||
|
if (ce_len < pathlen)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
ce_len -= pathlen;
|
||||||
|
ce_name = ce->name + pathlen;
|
||||||
|
|
||||||
|
len = tree_entry_len(n);
|
||||||
|
return df_name_compare(ce_name, ce_len, S_IFREG, n->path, len, n->mode);
|
||||||
|
}
|
||||||
|
|
||||||
static int compare_entry(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n)
|
static int compare_entry(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n)
|
||||||
{
|
{
|
||||||
int cmp = do_compare_entry(ce, info, n);
|
int cmp = do_compare_entry(ce, info, n);
|
||||||
@ -661,8 +695,19 @@ static int find_cache_pos(struct traverse_info *info,
|
|||||||
++o->cache_bottom;
|
++o->cache_bottom;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!ce_in_traverse_path(ce, info))
|
if (!ce_in_traverse_path(ce, info)) {
|
||||||
|
/*
|
||||||
|
* Check if we can skip future cache checks
|
||||||
|
* (because we're already past all possible
|
||||||
|
* entries in the traverse path).
|
||||||
|
*/
|
||||||
|
if (info->traverse_path) {
|
||||||
|
if (strncmp(ce->name, info->traverse_path,
|
||||||
|
info->pathlen) > 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
ce_name = ce->name + pfxlen;
|
ce_name = ce->name + pfxlen;
|
||||||
ce_slash = strchr(ce_name, '/');
|
ce_slash = strchr(ce_name, '/');
|
||||||
if (ce_slash)
|
if (ce_slash)
|
||||||
|
Reference in New Issue
Block a user