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 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.6 ms ± 0.2 ms [User: 2.5 ms, System: 3.0 ms] Range (min … max): 5.2 ms … 6.0 ms 89 runs Benchmark 2: update-ref: create many refs (refcount = 100, revision = HEAD~) Time (mean ± σ): 19.2 ms ± 0.3 ms [User: 8.6 ms, System: 10.4 ms] Range (min … max): 18.4 ms … 19.8 ms 63 runs Benchmark 3: update-ref: create many refs (refcount = 10000, revision = HEAD~) Time (mean ± σ): 1.310 s ± 0.014 s [User: 0.566 s, System: 0.724 s] Range (min … max): 1.291 s … 1.342 s 10 runs Benchmark 4: update-ref: create many refs (refcount = 1, revision = HEAD) Time (mean ± σ): 5.7 ms ± 0.4 ms [User: 2.6 ms, System: 3.1 ms] Range (min … max): 5.1 ms … 9.5 ms 91 runs Benchmark 5: update-ref: create many refs (refcount = 100, revision = HEAD) Time (mean ± σ): 15.2 ms ± 0.3 ms [User: 7.0 ms, System: 8.1 ms] Range (min … max): 14.3 ms … 17.1 ms 71 runs Benchmark 6: update-ref: create many refs (refcount = 10000, revision = HEAD) Time (mean ± σ): 916.1 ms ± 11.0 ms [User: 420.8 ms, System: 495.0 ms] Range (min … max): 906.9 ms … 943.8 ms 10 runs Summary update-ref: create many refs (refcount = 1, revision = HEAD~) ran 1.01 ± 0.09 times faster than update-ref: create many refs (refcount = 1, revision = HEAD) 2.72 ± 0.11 times faster than update-ref: create many refs (refcount = 100, revision = HEAD) 3.42 ± 0.13 times faster than update-ref: create many refs (refcount = 100, revision = HEAD~) 163.59 ± 5.62 times faster than update-ref: create many refs (refcount = 10000, revision = HEAD) 233.91 ± 7.92 times faster than update-ref: create many refs (refcount = 10000, revision = HEAD~) --- reftable/stack.c | 10 +++++++++- reftable/stack.h | 1 + reftable/system.h | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/reftable/stack.c b/reftable/stack.c index b1ee247601..a51a2dbf1f 100644 --- a/reftable/stack.c +++ b/reftable/stack.c @@ -175,6 +175,7 @@ void reftable_stack_destroy(struct reftable_stack *st) st->readers_len = 0; FREE_AND_NULL(st->readers); } + stat_validity_clear(&st->list_validity); FREE_AND_NULL(st->list_file); FREE_AND_NULL(st->reftable_dir); reftable_free(st); @@ -374,6 +375,8 @@ static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st, sleep_millisec(delay); } + stat_validity_update(&st->list_validity, fd); + out: if (fd >= 0) close(fd); @@ -388,8 +391,13 @@ static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st, static int stack_uptodate(struct reftable_stack *st) { char **names = NULL; - int err = read_lines(st->list_file, &names); + int err; int i = 0; + + if (stat_validity_check(&st->list_validity, st->list_file)) + return 0; + + err = read_lines(st->list_file, &names); if (err < 0) return err; diff --git a/reftable/stack.h b/reftable/stack.h index f57005846e..3f80cc598a 100644 --- a/reftable/stack.h +++ b/reftable/stack.h @@ -14,6 +14,7 @@ license that can be found in the LICENSE file or at #include "reftable-stack.h" struct reftable_stack { + struct stat_validity list_validity; char *list_file; char *reftable_dir; int disable_auto_compact; diff --git a/reftable/system.h b/reftable/system.h index 6b74a81514..2cc7adf271 100644 --- a/reftable/system.h +++ b/reftable/system.h @@ -12,6 +12,7 @@ license that can be found in the LICENSE file or at /* This header glues the reftable library to the rest of Git */ #include "git-compat-util.h" +#include "statinfo.h" #include "strbuf.h" #include "hash-ll.h" /* hash ID, sizes.*/ #include "dir.h" /* remove_dir_recursively, for tests.*/ -- 2.43.GIT
Attachment:
signature.asc
Description: PGP signature