Skip to content

Commit 201155c

Browse files
committed
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
2 parents e01c6b1 + a672095 commit 201155c

File tree

3 files changed

+56
-3
lines changed

3 files changed

+56
-3
lines changed

tree-walk.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
320320
struct tree_desc_x *tx = xcalloc(n, sizeof(*tx));
321321
struct strbuf base = STRBUF_INIT;
322322
int interesting = 1;
323+
char *traverse_path;
323324

324325
for (i = 0; i < n; i++)
325326
tx[i].d = t[i];
@@ -329,7 +330,11 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
329330
make_traverse_path(base.buf, info->prev, &info->name);
330331
base.buf[info->pathlen-1] = '/';
331332
strbuf_setlen(&base, info->pathlen);
333+
traverse_path = xstrndup(base.buf, info->pathlen);
334+
} else {
335+
traverse_path = xstrndup(info->name.path, info->pathlen);
332336
}
337+
info->traverse_path = traverse_path;
333338
for (;;) {
334339
int trees_used;
335340
unsigned long mask, dirmask;
@@ -411,6 +416,8 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
411416
for (i = 0; i < n; i++)
412417
free_extended_entry(tx + i);
413418
free(tx);
419+
free(traverse_path);
420+
info->traverse_path = NULL;
414421
strbuf_release(&base);
415422
return error;
416423
}

tree-walk.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ enum follow_symlinks_result {
5959
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);
6060

6161
struct traverse_info {
62+
const char *traverse_path;
6263
struct traverse_info *prev;
6364
struct name_entry name;
6465
int pathlen;

unpack-trees.c

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -498,13 +498,14 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,
498498
* itself - the caller needs to do the final check for the cache
499499
* entry having more data at the end!
500500
*/
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)
502502
{
503503
int len, pathlen, ce_len;
504504
const char *ce_name;
505505

506506
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);
508509
if (cmp)
509510
return cmp;
510511
}
@@ -522,6 +523,39 @@ static int do_compare_entry(const struct cache_entry *ce, const struct traverse_
522523
return df_name_compare(ce_name, ce_len, S_IFREG, n->path, len, n->mode);
523524
}
524525

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+
525559
static int compare_entry(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n)
526560
{
527561
int cmp = do_compare_entry(ce, info, n);
@@ -661,8 +695,19 @@ static int find_cache_pos(struct traverse_info *info,
661695
++o->cache_bottom;
662696
continue;
663697
}
664-
if (!ce_in_traverse_path(ce, info))
698+
if (!ce_in_traverse_path(ce, info)) {
699+
/*
700+
* Check if we can skip future cache checks
701+
* (because we're already past all possible
702+
* entries in the traverse path).
703+
*/
704+
if (info->traverse_path) {
705+
if (strncmp(ce->name, info->traverse_path,
706+
info->pathlen) > 0)
707+
break;
708+
}
665709
continue;
710+
}
666711
ce_name = ce->name + pfxlen;
667712
ce_slash = strchr(ce_name, '/');
668713
if (ce_slash)

0 commit comments

Comments
 (0)