oidset: use khash

Reimplement oidset using khash.h in order to reduce its memory footprint
and make it faster.

Performance of a command that mainly checks for duplicate objects using
an oidset, with master and Clang 6.0.1:

  $ cmd="./git-cat-file --batch-all-objects --unordered --buffer --batch-check='%(objectname)'"

  $ /usr/bin/time $cmd >/dev/null
  0.22user 0.03system 0:00.25elapsed 99%CPU (0avgtext+0avgdata 48484maxresident)k
  0inputs+0outputs (0major+11204minor)pagefaults 0swaps

  $ hyperfine "$cmd"
  Benchmark #1: ./git-cat-file --batch-all-objects --unordered --buffer --batch-check='%(objectname)'

    Time (mean ± σ):     250.0 ms ±   6.0 ms    [User: 225.9 ms, System: 23.6 ms]

    Range (min … max):   242.0 ms … 261.1 ms

And with this patch:

  $ /usr/bin/time $cmd >/dev/null
  0.14user 0.00system 0:00.15elapsed 100%CPU (0avgtext+0avgdata 41396maxresident)k
  0inputs+0outputs (0major+8318minor)pagefaults 0swaps

  $ hyperfine "$cmd"
  Benchmark #1: ./git-cat-file --batch-all-objects --unordered --buffer --batch-check='%(objectname)'

    Time (mean ± σ):     151.9 ms ±   4.9 ms    [User: 130.5 ms, System: 21.2 ms]

    Range (min … max):   148.2 ms … 170.4 ms

Initial-patch-by: Jeff King <peff@peff.net>
Signed-off-by: Rene Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
René Scharfe 2018-10-04 17:13:06 +02:00 committed by Junio C Hamano
parent 9249ca26ac
commit 8b2f8cbcb1
2 changed files with 40 additions and 30 deletions

View File

@ -3,38 +3,28 @@
int oidset_contains(const struct oidset *set, const struct object_id *oid) int oidset_contains(const struct oidset *set, const struct object_id *oid)
{ {
if (!set->map.map.tablesize) khiter_t pos = kh_get_oid(&set->set, *oid);
return 0; return pos != kh_end(&set->set);
return !!oidmap_get(&set->map, oid);
} }
int oidset_insert(struct oidset *set, const struct object_id *oid) int oidset_insert(struct oidset *set, const struct object_id *oid)
{ {
struct oidmap_entry *entry; int added;
kh_put_oid(&set->set, *oid, &added);
if (!set->map.map.tablesize) return !added;
oidmap_init(&set->map, 0);
else if (oidset_contains(set, oid))
return 1;
entry = xmalloc(sizeof(*entry));
oidcpy(&entry->oid, oid);
oidmap_put(&set->map, entry);
return 0;
} }
int oidset_remove(struct oidset *set, const struct object_id *oid) int oidset_remove(struct oidset *set, const struct object_id *oid)
{ {
struct oidmap_entry *entry; khiter_t pos = kh_get_oid(&set->set, *oid);
if (pos == kh_end(&set->set))
entry = oidmap_remove(&set->map, oid); return 0;
free(entry); kh_del_oid(&set->set, pos);
return 1;
return (entry != NULL);
} }
void oidset_clear(struct oidset *set) void oidset_clear(struct oidset *set)
{ {
oidmap_free(&set->map, 1); kh_release_oid(&set->set);
oidset_init(set, 0);
} }

View File

@ -1,7 +1,8 @@
#ifndef OIDSET_H #ifndef OIDSET_H
#define OIDSET_H #define OIDSET_H
#include "oidmap.h" #include "hashmap.h"
#include "khash.h"
/** /**
* This API is similar to sha1-array, in that it maintains a set of object ids * This API is similar to sha1-array, in that it maintains a set of object ids
@ -15,19 +16,33 @@
* table overhead. * table overhead.
*/ */
static inline unsigned int oid_hash(struct object_id oid)
{
return sha1hash(oid.hash);
}
static inline int oid_equal(struct object_id a, struct object_id b)
{
return oideq(&a, &b);
}
KHASH_INIT(oid, struct object_id, int, 0, oid_hash, oid_equal)
/** /**
* A single oidset; should be zero-initialized (or use OIDSET_INIT). * A single oidset; should be zero-initialized (or use OIDSET_INIT).
*/ */
struct oidset { struct oidset {
struct oidmap map; kh_oid_t set;
}; };
#define OIDSET_INIT { OIDMAP_INIT } #define OIDSET_INIT { { 0 } }
static inline void oidset_init(struct oidset *set, size_t initial_size) static inline void oidset_init(struct oidset *set, size_t initial_size)
{ {
oidmap_init(&set->map, initial_size); memset(&set->set, 0, sizeof(set->set));
if (initial_size)
kh_resize_oid(&set->set, initial_size);
} }
/** /**
@ -58,19 +73,24 @@ int oidset_remove(struct oidset *set, const struct object_id *oid);
void oidset_clear(struct oidset *set); void oidset_clear(struct oidset *set);
struct oidset_iter { struct oidset_iter {
struct oidmap_iter m_iter; kh_oid_t *set;
khiter_t iter;
}; };
static inline void oidset_iter_init(struct oidset *set, static inline void oidset_iter_init(struct oidset *set,
struct oidset_iter *iter) struct oidset_iter *iter)
{ {
oidmap_iter_init(&set->map, &iter->m_iter); iter->set = &set->set;
iter->iter = kh_begin(iter->set);
} }
static inline struct object_id *oidset_iter_next(struct oidset_iter *iter) static inline struct object_id *oidset_iter_next(struct oidset_iter *iter)
{ {
struct oidmap_entry *e = oidmap_iter_next(&iter->m_iter); for (; iter->iter != kh_end(iter->set); iter->iter++) {
return e ? &e->oid : NULL; if (kh_exist(iter->set, iter->iter))
return &kh_key(iter->set, iter->iter++);
}
return NULL;
} }
static inline struct object_id *oidset_iter_first(struct oidset *set, static inline struct object_id *oidset_iter_first(struct oidset *set,