]> git.ipfire.org Git - thirdparty/git.git/commitdiff
reftable/record: reuse refname when decoding
authorPatrick Steinhardt <ps@pks.im>
Mon, 4 Mar 2024 10:49:22 +0000 (11:49 +0100)
committerJunio C Hamano <gitster@pobox.com>
Mon, 4 Mar 2024 18:19:40 +0000 (10:19 -0800)
When decoding a reftable record we will first release the user-provided
record and then decode the new record into it. This is quite inefficient
as we basically need to reallocate at least the refname every time.

Refactor the function to start tracking the refname capacity. Like this,
we can stow away the refname, release, restore and then grow the refname
to the required number of bytes via `REFTABLE_ALLOC_GROW()`.

This refactoring is safe to do because all functions that assigning to
the refname will first call `reftable_ref_record_release()`, which will
zero out the complete record after releasing memory.

This change results in a nice speedup when iterating over 1 million
refs:

  Benchmark 1: show-ref: single matching ref (revision = HEAD~)

    Time (mean ± σ):     124.0 ms ±   3.9 ms    [User: 121.1 ms, System: 2.7 ms]
    Range (min … max):   120.4 ms … 152.7 ms    1000 runs

  Benchmark 2: show-ref: single matching ref (revision = HEAD)
    Time (mean ± σ):     114.4 ms ±   3.7 ms    [User: 111.5 ms, System: 2.7 ms]
    Range (min … max):   111.0 ms … 152.1 ms    1000 runs

  Summary
    show-ref: single matching ref (revision = HEAD) ran
      1.08 ± 0.05 times faster than show-ref: single matching ref (revision = HEAD~)

Furthermore, with this change we now perform a mostly constant number of
allocations when iterating. Before this change:

  HEAP SUMMARY:
      in use at exit: 13,603 bytes in 125 blocks
    total heap usage: 1,006,620 allocs, 1,006,495 frees, 25,398,363 bytes allocated

After this change:

  HEAP SUMMARY:
      in use at exit: 13,603 bytes in 125 blocks
    total heap usage: 6,623 allocs, 6,498 frees, 509,592 bytes allocated

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
reftable/record.c
reftable/reftable-record.h

index d6bb42e887423d95f2fcc1b2b146505ab4bef62a..2b52e47c30ca82b96b1e881a2d54b42d67b37d8f 100644 (file)
@@ -368,16 +368,24 @@ static int reftable_ref_record_decode(void *rec, struct strbuf key,
        struct reftable_ref_record *r = rec;
        struct string_view start = in;
        uint64_t update_index = 0;
-       int n = get_var_int(&update_index, &in);
+       const char *refname = NULL;
+       size_t refname_cap = 0;
+       int n;
+
+       assert(hash_size > 0);
+
+       n = get_var_int(&update_index, &in);
        if (n < 0)
                return n;
        string_view_consume(&in, n);
 
+       SWAP(refname, r->refname);
+       SWAP(refname_cap, r->refname_cap);
        reftable_ref_record_release(r);
+       SWAP(r->refname, refname);
+       SWAP(r->refname_cap, refname_cap);
 
-       assert(hash_size > 0);
-
-       r->refname = reftable_malloc(key.len + 1);
+       REFTABLE_ALLOC_GROW(r->refname, key.len + 1, r->refname_cap);
        memcpy(r->refname, key.buf, key.len);
        r->refname[key.len] = 0;
 
index bb6e99acd3151ec75a7a2dc60e930064b7322e60..e657001d42fc9e640052092c59db17121e60cf12 100644 (file)
@@ -22,6 +22,7 @@ https://developers.google.com/open-source/licenses/bsd
 /* reftable_ref_record holds a ref database entry target_value */
 struct reftable_ref_record {
        char *refname; /* Name of the ref, malloced. */
+       size_t refname_cap;
        uint64_t update_index; /* Logical timestamp at which this value is
                                * written */