speed up alt_odb_usable() with many alternates
With many alternates, the duplicate check in alt_odb_usable() wastes many cycles doing repeated fspathcmp() on every existing alternate. Use a khash to speed up lookups by odb->path. Since the kh_put_* API uses the supplied key without duplicating it, we also take advantage of it to replace both xstrdup() and strbuf_release() in link_alt_odb_entry() with strbuf_detach() to avoid the allocation and copy. In a test repository with 50K alternates and each of those 50K alternates having one alternate each (for a total of 100K total alternates); this speeds up lookup of a non-existent blob from over 16 minutes to roughly 2.7 seconds on my busy workstation. Note: all underlying git object directories were small and unpacked with only loose objects and no packs. Having to load packs increases times significantly. Signed-off-by: Eric Wong <e@80x24.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>maint
parent
670b81a890
commit
cf2dc1c238
10
dir.c
10
dir.c
|
@ -84,11 +84,21 @@ int fspathcmp(const char *a, const char *b)
|
||||||
return ignore_case ? strcasecmp(a, b) : strcmp(a, b);
|
return ignore_case ? strcasecmp(a, b) : strcmp(a, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int fspatheq(const char *a, const char *b)
|
||||||
|
{
|
||||||
|
return !fspathcmp(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
int fspathncmp(const char *a, const char *b, size_t count)
|
int fspathncmp(const char *a, const char *b, size_t count)
|
||||||
{
|
{
|
||||||
return ignore_case ? strncasecmp(a, b, count) : strncmp(a, b, count);
|
return ignore_case ? strncasecmp(a, b, count) : strncmp(a, b, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned int fspathhash(const char *str)
|
||||||
|
{
|
||||||
|
return ignore_case ? strihash(str) : strhash(str);
|
||||||
|
}
|
||||||
|
|
||||||
int git_fnmatch(const struct pathspec_item *item,
|
int git_fnmatch(const struct pathspec_item *item,
|
||||||
const char *pattern, const char *string,
|
const char *pattern, const char *string,
|
||||||
int prefix)
|
int prefix)
|
||||||
|
|
2
dir.h
2
dir.h
|
@ -489,7 +489,9 @@ int remove_dir_recursively(struct strbuf *path, int flag);
|
||||||
int remove_path(const char *path);
|
int remove_path(const char *path);
|
||||||
|
|
||||||
int fspathcmp(const char *a, const char *b);
|
int fspathcmp(const char *a, const char *b);
|
||||||
|
int fspatheq(const char *a, const char *b);
|
||||||
int fspathncmp(const char *a, const char *b, size_t count);
|
int fspathncmp(const char *a, const char *b, size_t count);
|
||||||
|
unsigned int fspathhash(const char *str);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The prefix part of pattern must not contains wildcards.
|
* The prefix part of pattern must not contains wildcards.
|
||||||
|
|
|
@ -517,9 +517,9 @@ const char *loose_object_path(struct repository *r, struct strbuf *buf,
|
||||||
*/
|
*/
|
||||||
static int alt_odb_usable(struct raw_object_store *o,
|
static int alt_odb_usable(struct raw_object_store *o,
|
||||||
struct strbuf *path,
|
struct strbuf *path,
|
||||||
const char *normalized_objdir)
|
const char *normalized_objdir, khiter_t *pos)
|
||||||
{
|
{
|
||||||
struct object_directory *odb;
|
int r;
|
||||||
|
|
||||||
/* Detect cases where alternate disappeared */
|
/* Detect cases where alternate disappeared */
|
||||||
if (!is_directory(path->buf)) {
|
if (!is_directory(path->buf)) {
|
||||||
|
@ -533,14 +533,20 @@ static int alt_odb_usable(struct raw_object_store *o,
|
||||||
* Prevent the common mistake of listing the same
|
* Prevent the common mistake of listing the same
|
||||||
* thing twice, or object directory itself.
|
* thing twice, or object directory itself.
|
||||||
*/
|
*/
|
||||||
for (odb = o->odb; odb; odb = odb->next) {
|
if (!o->odb_by_path) {
|
||||||
if (!fspathcmp(path->buf, odb->path))
|
khiter_t p;
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (!fspathcmp(path->buf, normalized_objdir))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return 1;
|
o->odb_by_path = kh_init_odb_path_map();
|
||||||
|
assert(!o->odb->next);
|
||||||
|
p = kh_put_odb_path_map(o->odb_by_path, o->odb->path, &r);
|
||||||
|
assert(r == 1); /* never used */
|
||||||
|
kh_value(o->odb_by_path, p) = o->odb;
|
||||||
|
}
|
||||||
|
if (fspatheq(path->buf, normalized_objdir))
|
||||||
|
return 0;
|
||||||
|
*pos = kh_put_odb_path_map(o->odb_by_path, path->buf, &r);
|
||||||
|
/* r: 0 = exists, 1 = never used, 2 = deleted */
|
||||||
|
return r == 0 ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -566,6 +572,7 @@ static int link_alt_odb_entry(struct repository *r, const char *entry,
|
||||||
{
|
{
|
||||||
struct object_directory *ent;
|
struct object_directory *ent;
|
||||||
struct strbuf pathbuf = STRBUF_INIT;
|
struct strbuf pathbuf = STRBUF_INIT;
|
||||||
|
khiter_t pos;
|
||||||
|
|
||||||
if (!is_absolute_path(entry) && relative_base) {
|
if (!is_absolute_path(entry) && relative_base) {
|
||||||
strbuf_realpath(&pathbuf, relative_base, 1);
|
strbuf_realpath(&pathbuf, relative_base, 1);
|
||||||
|
@ -587,23 +594,25 @@ static int link_alt_odb_entry(struct repository *r, const char *entry,
|
||||||
while (pathbuf.len && pathbuf.buf[pathbuf.len - 1] == '/')
|
while (pathbuf.len && pathbuf.buf[pathbuf.len - 1] == '/')
|
||||||
strbuf_setlen(&pathbuf, pathbuf.len - 1);
|
strbuf_setlen(&pathbuf, pathbuf.len - 1);
|
||||||
|
|
||||||
if (!alt_odb_usable(r->objects, &pathbuf, normalized_objdir)) {
|
if (!alt_odb_usable(r->objects, &pathbuf, normalized_objdir, &pos)) {
|
||||||
strbuf_release(&pathbuf);
|
strbuf_release(&pathbuf);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
CALLOC_ARRAY(ent, 1);
|
CALLOC_ARRAY(ent, 1);
|
||||||
ent->path = xstrdup(pathbuf.buf);
|
/* pathbuf.buf is already in r->objects->odb_by_path */
|
||||||
|
ent->path = strbuf_detach(&pathbuf, NULL);
|
||||||
|
|
||||||
/* add the alternate entry */
|
/* add the alternate entry */
|
||||||
*r->objects->odb_tail = ent;
|
*r->objects->odb_tail = ent;
|
||||||
r->objects->odb_tail = &(ent->next);
|
r->objects->odb_tail = &(ent->next);
|
||||||
ent->next = NULL;
|
ent->next = NULL;
|
||||||
|
assert(r->objects->odb_by_path);
|
||||||
|
kh_value(r->objects->odb_by_path, pos) = ent;
|
||||||
|
|
||||||
/* recursively add alternates */
|
/* recursively add alternates */
|
||||||
read_info_alternates(r, pathbuf.buf, depth + 1);
|
read_info_alternates(r, ent->path, depth + 1);
|
||||||
|
|
||||||
strbuf_release(&pathbuf);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
#include "oid-array.h"
|
#include "oid-array.h"
|
||||||
#include "strbuf.h"
|
#include "strbuf.h"
|
||||||
#include "thread-utils.h"
|
#include "thread-utils.h"
|
||||||
|
#include "khash.h"
|
||||||
|
#include "dir.h"
|
||||||
|
|
||||||
struct object_directory {
|
struct object_directory {
|
||||||
struct object_directory *next;
|
struct object_directory *next;
|
||||||
|
@ -30,6 +32,9 @@ struct object_directory {
|
||||||
char *path;
|
char *path;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
KHASH_INIT(odb_path_map, const char * /* key: odb_path */,
|
||||||
|
struct object_directory *, 1, fspathhash, fspatheq);
|
||||||
|
|
||||||
void prepare_alt_odb(struct repository *r);
|
void prepare_alt_odb(struct repository *r);
|
||||||
char *compute_alternate_path(const char *path, struct strbuf *err);
|
char *compute_alternate_path(const char *path, struct strbuf *err);
|
||||||
typedef int alt_odb_fn(struct object_directory *, void *);
|
typedef int alt_odb_fn(struct object_directory *, void *);
|
||||||
|
@ -116,6 +121,8 @@ struct raw_object_store {
|
||||||
*/
|
*/
|
||||||
struct object_directory *odb;
|
struct object_directory *odb;
|
||||||
struct object_directory **odb_tail;
|
struct object_directory **odb_tail;
|
||||||
|
kh_odb_path_map_t *odb_by_path;
|
||||||
|
|
||||||
int loaded_alternates;
|
int loaded_alternates;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
2
object.c
2
object.c
|
@ -511,6 +511,8 @@ static void free_object_directories(struct raw_object_store *o)
|
||||||
free_object_directory(o->odb);
|
free_object_directory(o->odb);
|
||||||
o->odb = next;
|
o->odb = next;
|
||||||
}
|
}
|
||||||
|
kh_destroy_odb_path_map(o->odb_by_path);
|
||||||
|
o->odb_by_path = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void raw_object_store_clear(struct raw_object_store *o)
|
void raw_object_store_clear(struct raw_object_store *o)
|
||||||
|
|
Loading…
Reference in New Issue