Skip to content

Commit 6fdfaf1

Browse files
pks-tgitster
authored andcommitted
reftable/stack: use stat info to avoid re-reading stack list
Whenever we call into the refs interfaces we potentially have to reload refs in case they have been concurrently modified, either in-process or externally. While this happens somewhat automatically for loose refs because we simply try to re-read the files, the "packed" backend will reload its snapshot of the packed-refs file in case its stat info has changed since last reading it. In the reftable backend we have a similar mechanism that is provided by `reftable_stack_reload()`. This function will read the list of stacks from "tables.list" and, if they have changed from the currently stored list, reload the stacks. This is heavily inefficient though, as we have to check whether the stack is up-to-date on basically every read and thus keep on re-reading the file all the time even if it didn't change at all. We can do better and use the same stat(3P)-based mechanism that the "packed" backend uses. Instead of reading the file, we will only open the file descriptor, fstat(3P) it, and then compare the info against the cached value from the last time we have updated the stack. This should always work alright because "tables.list" is updated atomically via a rename, so even if the ctime or mtime wasn't granular enough to identify a change, at least the inode number or file size should have changed. This change significantly speeds up operations where many refs are read, like when using git-update-ref(1). The following benchmark creates N refs in an otherwise-empty repository via `git update-ref --stdin`: Benchmark 1: update-ref: create many refs (refcount = 1, revision = HEAD~) Time (mean ± σ): 5.1 ms ± 0.2 ms [User: 2.4 ms, System: 2.6 ms] Range (min … max): 4.8 ms … 7.2 ms 109 runs Benchmark 2: update-ref: create many refs (refcount = 100, revision = HEAD~) Time (mean ± σ): 19.1 ms ± 0.9 ms [User: 8.9 ms, System: 9.9 ms] Range (min … max): 18.4 ms … 26.7 ms 72 runs Benchmark 3: update-ref: create many refs (refcount = 10000, revision = HEAD~) Time (mean ± σ): 1.336 s ± 0.018 s [User: 0.590 s, System: 0.724 s] Range (min … max): 1.314 s … 1.373 s 10 runs Benchmark 4: update-ref: create many refs (refcount = 1, revision = HEAD) Time (mean ± σ): 5.1 ms ± 0.2 ms [User: 2.4 ms, System: 2.6 ms] Range (min … max): 4.8 ms … 7.2 ms 109 runs Benchmark 5: update-ref: create many refs (refcount = 100, revision = HEAD) Time (mean ± σ): 14.8 ms ± 0.2 ms [User: 7.1 ms, System: 7.5 ms] Range (min … max): 14.2 ms … 15.2 ms 82 runs Benchmark 6: update-ref: create many refs (refcount = 10000, revision = HEAD) Time (mean ± σ): 927.6 ms ± 5.3 ms [User: 437.8 ms, System: 489.5 ms] Range (min … max): 919.4 ms … 936.4 ms 10 runs Summary update-ref: create many refs (refcount = 1, revision = HEAD) ran 1.00 ± 0.07 times faster than update-ref: create many refs (refcount = 1, revision = HEAD~) 2.89 ± 0.14 times faster than update-ref: create many refs (refcount = 100, revision = HEAD) 3.74 ± 0.25 times faster than update-ref: create many refs (refcount = 100, revision = HEAD~) 181.26 ± 8.30 times faster than update-ref: create many refs (refcount = 10000, revision = HEAD) 261.01 ± 12.35 times faster than update-ref: create many refs (refcount = 10000, revision = HEAD~) Signed-off-by: Patrick Steinhardt <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent c5b5d5f commit 6fdfaf1

File tree

3 files changed

+13
-1
lines changed

3 files changed

+13
-1
lines changed

reftable/stack.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ void reftable_stack_destroy(struct reftable_stack *st)
175175
st->readers_len = 0;
176176
FREE_AND_NULL(st->readers);
177177
}
178+
stat_validity_clear(&st->list_validity);
178179
FREE_AND_NULL(st->list_file);
179180
FREE_AND_NULL(st->reftable_dir);
180181
reftable_free(st);
@@ -374,7 +375,11 @@ static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st,
374375
sleep_millisec(delay);
375376
}
376377

378+
stat_validity_update(&st->list_validity, fd);
379+
377380
out:
381+
if (err)
382+
stat_validity_clear(&st->list_validity);
378383
if (fd >= 0)
379384
close(fd);
380385
free_names(names);
@@ -388,8 +393,13 @@ static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st,
388393
static int stack_uptodate(struct reftable_stack *st)
389394
{
390395
char **names = NULL;
391-
int err = read_lines(st->list_file, &names);
396+
int err;
392397
int i = 0;
398+
399+
if (stat_validity_check(&st->list_validity, st->list_file))
400+
return 0;
401+
402+
err = read_lines(st->list_file, &names);
393403
if (err < 0)
394404
return err;
395405

reftable/stack.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ license that can be found in the LICENSE file or at
1414
#include "reftable-stack.h"
1515

1616
struct reftable_stack {
17+
struct stat_validity list_validity;
1718
char *list_file;
1819
char *reftable_dir;
1920
int disable_auto_compact;

reftable/system.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ license that can be found in the LICENSE file or at
1212
/* This header glues the reftable library to the rest of Git */
1313

1414
#include "git-compat-util.h"
15+
#include "statinfo.h"
1516
#include "strbuf.h"
1617
#include "hash-ll.h" /* hash ID, sizes.*/
1718
#include "dir.h" /* remove_dir_recursively, for tests.*/

0 commit comments

Comments
 (0)