Skip to content

Commit

Permalink
Merge branch 'dt/unpack-compare-entry-optim'
Browse files Browse the repository at this point in the history
"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
  • Loading branch information
gitster committed Feb 3, 2016
2 parents e01c6b1 + a672095 commit 201155c
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 3 deletions.
7 changes: 7 additions & 0 deletions tree-walk.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 strbuf base = STRBUF_INIT;
int interesting = 1;
char *traverse_path;

for (i = 0; i < n; i++)
tx[i].d = t[i];
Expand All @@ -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);
base.buf[info->pathlen-1] = '/';
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 (;;) {
int trees_used;
unsigned long mask, dirmask;
Expand Down Expand Up @@ -411,6 +416,8 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
for (i = 0; i < n; i++)
free_extended_entry(tx + i);
free(tx);
free(traverse_path);
info->traverse_path = NULL;
strbuf_release(&base);
return error;
}
Expand Down
1 change: 1 addition & 0 deletions tree-walk.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

struct traverse_info {
const char *traverse_path;
struct traverse_info *prev;
struct name_entry name;
int pathlen;
Expand Down
51 changes: 48 additions & 3 deletions unpack-trees.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
* 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;
const char *ce_name;

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)
return cmp;
}
Expand All @@ -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);
}

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)
{
int cmp = do_compare_entry(ce, info, n);
Expand Down Expand Up @@ -661,8 +695,19 @@ static int find_cache_pos(struct traverse_info *info,
++o->cache_bottom;
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;
}
ce_name = ce->name + pfxlen;
ce_slash = strchr(ce_name, '/');
if (ce_slash)
Expand Down

0 comments on commit 201155c

Please sign in to comment.