diff options
author | Junio C Hamano <gitster@pobox.com> | 2016-02-03 14:16:06 -0800 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2016-02-03 14:16:06 -0800 |
commit | 201155cd1164572086a8a5fa1708b93d68b64f08 (patch) | |
tree | 8bf0b7ba5a89248b231704119fb4e8eea06303c0 | |
parent | e01c6b15c97e30baedc45021e6dcbd90140616cd (diff) | |
parent | a6720955f19ea10bf9569d04480deed25b1bccf7 (diff) | |
download | git-201155cd1164572086a8a5fa1708b93d68b64f08.tar.gz git-201155cd1164572086a8a5fa1708b93d68b64f08.tar.xz |
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
-rw-r--r-- | tree-walk.c | 7 | ||||
-rw-r--r-- | tree-walk.h | 1 | ||||
-rw-r--r-- | unpack-trees.c | 51 |
3 files changed, 56 insertions, 3 deletions
diff --git a/tree-walk.c b/tree-walk.c index 6dccd2d5d..cd4bb2c38 100644 --- a/tree-walk.c +++ b/tree-walk.c @@ -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]; @@ -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; @@ -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; } diff --git a/tree-walk.h b/tree-walk.h index 3b2f7bf17..174eb617d 100644 --- a/tree-walk.h +++ b/tree-walk.h @@ -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; diff --git a/unpack-trees.c b/unpack-trees.c index 8e2032f4e..9f55cc28b 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -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; } @@ -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); @@ -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) |