do_compare_entry: use already-computed path

In traverse_trees, we generate the complete traverse path for a traverse_info. Later, in do_compare_entry, we used to go do a bunch of work to compare the traverse_info to a cache_entry's name without computing that path. But since we already have that path, we don't need to do all that work. Instead, we can just put the generated path into the traverse_info, and do the comparison more directly. We copy the path because prune_traversal might mutate `base`. This doesn't happen in any codepaths where do_compare_entry is called, but it's better to be safe. This makes git checkout much faster -- about 25% on Twitter's monorepo. Deeper directory trees are likely to benefit more than shallower ones. Signed-off-by: David Turner <dturner@twopensource.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

David Turner committed Dec 21, 2015 at 17:34 UTC d9c2bd560e1e7a3d4654fb6ef3f9037ad337eb01
3 files changed +44 -2
tree-walk.c
+7
@@ -320,6 +320,7 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
320 struct tree_desc_x *tx = xcalloc(n, sizeof(*tx));
321 struct strbuf base = STRBUF_INIT;
322 int interesting = 1;
323 + char *traverse_path;
324
325 for (i = 0; i < n; i++)
326 tx[i].d = t[i];
@@ -329,7 +330,11 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
330 make_traverse_path(base.buf, info->prev, &info->name);
331 base.buf[info->pathlen-1] = '/';
332 strbuf_setlen(&base, info->pathlen);
333 + traverse_path = xstrndup(base.buf, info->pathlen);
334 + } else {
335 + traverse_path = xstrndup(info->name.path, info->pathlen);
336 }
337 + info->traverse_path = traverse_path;
338 for (;;) {
339 int trees_used;
340 unsigned long mask, dirmask;
@@ -411,6 +416,8 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
416 for (i = 0; i < n; i++)
417 free_extended_entry(tx + i);
418 free(tx);
419 + free(traverse_path);
420 + info->traverse_path = NULL;
421 strbuf_release(&base);
422 return error;
423 }
tree-walk.h
+1
@@ -59,6 +59,7 @@ enum follow_symlinks_result {
59 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);
60
61 struct traverse_info {
62 + const char *traverse_path;
63 struct traverse_info *prev;
64 struct name_entry name;
65 int pathlen;
unpack-trees.c
+36 -2
@@ -498,13 +498,14 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,
498 * itself - the caller needs to do the final check for the cache
499 * entry having more data at the end!
500 */
501 -static int do_compare_entry(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n)
501 +static int do_compare_entry_piecewise(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n)
502 {
503 int len, pathlen, ce_len;
504 const char *ce_name;
505
506 if (info->prev) {
507 - int cmp = do_compare_entry(ce, info->prev, &info->name);
507 + int cmp = do_compare_entry_piecewise(ce, info->prev,
508 + &info->name);
509 if (cmp)
510 return cmp;
511 }
@@ -522,6 +523,39 @@ static int do_compare_entry(const struct cache_entry *ce, const struct traverse_
523 return df_name_compare(ce_name, ce_len, S_IFREG, n->path, len, n->mode);
524 }
525
526 +static int do_compare_entry(const struct cache_entry *ce,
527 + const struct traverse_info *info,
528 + const struct name_entry *n)
529 +{
530 + int len, pathlen, ce_len;
531 + const char *ce_name;
532 + int cmp;
533 +
534 + /*
535 + * If we have not precomputed the traverse path, it is quicker
536 + * to avoid doing so. But if we have precomputed it,
537 + * it is quicker to use the precomputed version.
538 + */
539 + if (!info->traverse_path)
540 + return do_compare_entry_piecewise(ce, info, n);
541 +
542 + cmp = strncmp(ce->name, info->traverse_path, info->pathlen);
543 + if (cmp)
544 + return cmp;
545 +
546 + pathlen = info->pathlen;
547 + ce_len = ce_namelen(ce);
548 +
549 + if (ce_len < pathlen)
550 + return -1;
551 +
552 + ce_len -= pathlen;
553 + ce_name = ce->name + pathlen;
554 +
555 + len = tree_entry_len(n);
556 + return df_name_compare(ce_name, ce_len, S_IFREG, n->path, len, n->mode);
557 +}
558 +
559 static int compare_entry(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n)
560 {
561 int cmp = do_compare_entry(ce, info, n);