From 71cc0bd2401aaf22ea8e49c134b65080a933f88e Mon Sep 17 00:00:00 2001 From: Takuto Ikuta Date: Tue, 30 Jan 2018 22:42:58 +0900 Subject: [PATCH] checkout.c: enable fscache for checkout again This is retry of #1419. I added flush_fscache macro to flush cached stats after disk writing with tests for regression reported in #1438 and #1442. git checkout checks each file path in sorted order, so cache flushing does not make performance worse unless we have large number of modified files in a directory containing many files. Using chromium repository, I tested `git checkout .` performance when I delete 10 files in different directories. With this patch: TotalSeconds: 4.307272 TotalSeconds: 4.4863595 TotalSeconds: 4.2975562 Avg: 4.36372923333333 Without this patch: TotalSeconds: 20.9705431 TotalSeconds: 22.4867685 TotalSeconds: 18.8968292 Avg: 20.7847136 I confirmed this patch passed all tests in t/ with core_fscache=1. Signed-off-by: Takuto Ikuta --- builtin/checkout.c | 2 ++ compat/win32/fscache.c | 12 ++++++++++++ compat/win32/fscache.h | 3 +++ entry.c | 3 +++ git-compat-util.h | 4 ++++ t/t7201-co.sh | 36 ++++++++++++++++++++++++++++++++++++ 6 files changed, 60 insertions(+) diff --git a/builtin/checkout.c b/builtin/checkout.c index 70d5785a5d95b1..95688d6e6ba73e 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -359,6 +359,7 @@ static int checkout_paths(const struct checkout_opts *opts, state.istate = &the_index; enable_delayed_checkout(&state); + enable_fscache(1); for (pos = 0; pos < active_nr; pos++) { struct cache_entry *ce = active_cache[pos]; if (ce->ce_flags & CE_MATCHED) { @@ -373,6 +374,7 @@ static int checkout_paths(const struct checkout_opts *opts, pos = skip_same_name(ce, pos) - 1; } } + enable_fscache(0); errs |= finish_delayed_checkout(&state); if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) diff --git a/compat/win32/fscache.c b/compat/win32/fscache.c index ee2b0dea0cfcc9..9ff9dfefd07d55 100644 --- a/compat/win32/fscache.c +++ b/compat/win32/fscache.c @@ -411,6 +411,18 @@ int fscache_enable(int enable) return result; } +/* + * Flush cached stats result when fscache is enabled. + */ +void fscache_flush() +{ + if (enabled) { + EnterCriticalSection(&mutex); + fscache_clear(); + LeaveCriticalSection(&mutex); + } +} + /* * Lstat replacement, uses the cache if enabled, otherwise redirects to * mingw_lstat. diff --git a/compat/win32/fscache.h b/compat/win32/fscache.h index 660ada053b4309..e8441345bff3bb 100644 --- a/compat/win32/fscache.h +++ b/compat/win32/fscache.h @@ -7,6 +7,9 @@ int fscache_enable(int enable); int fscache_enabled(const char *path); #define is_fscache_enabled(path) fscache_enabled(path) +void fscache_flush(); +#define flush_fscache() fscache_flush() + DIR *fscache_opendir(const char *dir); int fscache_lstat(const char *file_name, struct stat *buf); diff --git a/entry.c b/entry.c index 30211447ac8398..0d86909e800081 100644 --- a/entry.c +++ b/entry.c @@ -366,6 +366,9 @@ static int write_entry(struct cache_entry *ce, } finish: + // Flush cached lstat in fscache after writing disk. + flush_fscache(); + if (state->refresh_cache) { assert(state->istate); if (!fstat_done) diff --git a/git-compat-util.h b/git-compat-util.h index aebb3e2b1cec4a..6617487c243469 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -1282,6 +1282,10 @@ static inline int is_missing_file_error(int errno_) #define is_fscache_enabled(path) (0) #endif +#ifndef flush_fscache +#define flush_fscache() /* noop */ +#endif + extern int cmd_main(int, const char **); /* diff --git a/t/t7201-co.sh b/t/t7201-co.sh index 76c223c9675cd1..bca868e98be1f8 100755 --- a/t/t7201-co.sh +++ b/t/t7201-co.sh @@ -32,6 +32,42 @@ fill () { } +test_expect_success MINGW 'fscache flush cache' ' + + git init fscache-test && + cd fscache-test && + git config core.fscache 1 && + echo A > test.txt && + git add test.txt && + git commit -m A && + echo B >> test.txt && + git checkout . && + test -z "$(git status -s)" && + echo A > expect.txt && + test_cmp expect.txt test.txt && + cd .. && + rm -rf fscache-test +' + +test_expect_success MINGW 'fscache flush cache dir' ' + + git init fscache-test && + cd fscache-test && + git config core.fscache 1 && + echo A > test.txt && + git add test.txt && + git commit -m A && + rm test.txt && + mkdir test.txt && + touch test.txt/test.txt && + git checkout . && + test -z "$(git status -s)" && + echo A > expect.txt && + test_cmp expect.txt test.txt && + cd .. && + rm -rf fscache-test +' + test_expect_success setup ' fill x y z > same &&