Merge remote-tracking branch 'benpeart/fscache-per-thread-gfw'

This brings substantial wins in performance because the FSCache is now
per-thread, being merged to the primary thread only at the end, so we do
not have to lock (except while merging).

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This commit is contained in:
Johannes Schindelin
2018-11-16 14:38:19 +01:00
10 changed files with 254 additions and 145 deletions

View File

@@ -461,7 +461,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
die_in_unpopulated_submodule(&the_index, prefix);
die_path_inside_submodule(&the_index, &pathspec);
enable_fscache(1);
enable_fscache(0);
/* We do not really re-read the index but update the up-to-date flags */
preload_index(&the_index, &pathspec, 0);

View File

@@ -372,7 +372,7 @@ static int checkout_paths(const struct checkout_opts *opts,
state.istate = &the_index;
enable_delayed_checkout(&state);
enable_fscache(1);
enable_fscache(active_nr);
for (pos = 0; pos < active_nr; pos++) {
struct cache_entry *ce = active_cache[pos];
if (ce->ce_flags & CE_MATCHED) {
@@ -391,7 +391,7 @@ static int checkout_paths(const struct checkout_opts *opts,
pos = skip_same_name(ce, pos) - 1;
}
}
enable_fscache(0);
disable_fscache();
errs |= finish_delayed_checkout(&state, &nr_checkouts);
if (opts->count_checkout_paths) {

View File

@@ -1366,7 +1366,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
PATHSPEC_PREFER_FULL,
prefix, argv);
enable_fscache(1);
enable_fscache(0);
if (status_format != STATUS_FORMAT_PORCELAIN &&
status_format != STATUS_FORMAT_PORCELAIN_V2)
progress_flag = REFRESH_PROGRESS;
@@ -1407,7 +1407,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
wt_status_print(&s);
wt_status_collect_free_buffers(&s);
enable_fscache(0);
disable_fscache();
return 0;
}

View File

@@ -3,15 +3,27 @@
#include "../win32.h"
#include "fscache.h"
#include "config.h"
#include "../../mem-pool.h"
static int initialized;
static volatile long enabled;
static struct hashmap map;
static volatile long initialized;
static DWORD dwTlsIndex;
static CRITICAL_SECTION mutex;
static unsigned int lstat_requests;
static unsigned int opendir_requests;
static unsigned int fscache_requests;
static unsigned int fscache_misses;
/*
* Store one fscache per thread to avoid thread contention and locking.
* This is ok because multi-threaded access is 1) uncommon and 2) always
* splitting up the cache entries across multiple threads so there isn't
* any overlap between threads anyway.
*/
struct fscache {
volatile long enabled;
struct hashmap map;
struct mem_pool *mem_pool;
unsigned int lstat_requests;
unsigned int opendir_requests;
unsigned int fscache_requests;
unsigned int fscache_misses;
};
static struct trace_key trace_fscache = TRACE_KEY_INIT(FSCACHE);
/*
@@ -39,8 +51,6 @@ struct fsentry {
union {
/* Reference count of the directory listing. */
volatile long refcnt;
/* Handle to wait on the loading thread. */
HANDLE hwait;
struct {
/* More stat members (only used for file entries). */
off64_t st_size;
@@ -98,11 +108,11 @@ static void fsentry_init(struct fsentry *fse, struct fsentry *list,
/*
* Allocate an fsentry structure on the heap.
*/
static struct fsentry *fsentry_alloc(struct fsentry *list, const char *name,
static struct fsentry *fsentry_alloc(struct fscache *cache, struct fsentry *list, const char *name,
size_t len)
{
/* overallocate fsentry and copy the name to the end */
struct fsentry *fse = xmalloc(sizeof(struct fsentry) + len + 1);
struct fsentry *fse = mem_pool_alloc(cache->mem_pool, sizeof(struct fsentry) + len + 1);
char *nm = ((char*) fse) + sizeof(struct fsentry);
memcpy(nm, name, len);
nm[len] = 0;
@@ -125,27 +135,20 @@ inline static void fsentry_addref(struct fsentry *fse)
}
/*
* Release the reference to an fsentry, frees the memory if its the last ref.
* Release the reference to an fsentry.
*/
static void fsentry_release(struct fsentry *fse)
{
if (fse->list)
fse = fse->list;
if (InterlockedDecrement(&(fse->refcnt)))
return;
while (fse) {
struct fsentry *next = fse->next;
free(fse);
fse = next;
}
InterlockedDecrement(&(fse->refcnt));
}
/*
* Allocate and initialize an fsentry from a WIN32_FIND_DATA structure.
*/
static struct fsentry *fseentry_create_entry(struct fsentry *list,
static struct fsentry *fseentry_create_entry(struct fscache *cache, struct fsentry *list,
const WIN32_FIND_DATAW *fdata)
{
char buf[MAX_PATH * 3];
@@ -153,7 +156,7 @@ static struct fsentry *fseentry_create_entry(struct fsentry *list,
struct fsentry *fse;
len = xwcstoutf(buf, fdata->cFileName, ARRAY_SIZE(buf));
fse = fsentry_alloc(list, buf, len);
fse = fsentry_alloc(cache, list, buf, len);
/*
* On certain Windows versions, host directories mapped into
@@ -193,7 +196,7 @@ static struct fsentry *fseentry_create_entry(struct fsentry *list,
* Dir should not contain trailing '/'. Use an empty string for the current
* directory (not "."!).
*/
static struct fsentry *fsentry_create_list(const struct fsentry *dir,
static struct fsentry *fsentry_create_list(struct fscache *cache, const struct fsentry *dir,
int *dir_not_found)
{
wchar_t pattern[MAX_LONG_PATH + 2]; /* + 2 for "\*" */
@@ -232,13 +235,13 @@ static struct fsentry *fsentry_create_list(const struct fsentry *dir,
}
/* allocate object to hold directory listing */
list = fsentry_alloc(NULL, dir->name, dir->len);
list = fsentry_alloc(cache, NULL, dir->name, dir->len);
list->st_mode = S_IFDIR;
/* walk directory and build linked list of fsentry structures */
phead = &list->next;
do {
*phead = fseentry_create_entry(list, &fdata);
*phead = fseentry_create_entry(cache, list, &fdata);
phead = &(*phead)->next;
} while (FindNextFileW(h, &fdata));
@@ -250,7 +253,7 @@ static struct fsentry *fsentry_create_list(const struct fsentry *dir,
if (err == ERROR_NO_MORE_FILES)
return list;
/* otherwise free the list and return error */
/* otherwise release the list and return error */
fsentry_release(list);
errno = err_win_to_posix(err);
return NULL;
@@ -259,86 +262,66 @@ static struct fsentry *fsentry_create_list(const struct fsentry *dir,
/*
* Adds a directory listing to the cache.
*/
static void fscache_add(struct fsentry *fse)
static void fscache_add(struct fscache *cache, struct fsentry *fse)
{
if (fse->list)
fse = fse->list;
for (; fse; fse = fse->next)
hashmap_add(&map, fse);
hashmap_add(&cache->map, fse);
}
/*
* Clears the cache.
*/
static void fscache_clear(void)
static void fscache_clear(struct fscache *cache)
{
hashmap_free(&map, 1);
hashmap_init(&map, (hashmap_cmp_fn)fsentry_cmp, NULL, 0);
lstat_requests = opendir_requests = 0;
fscache_misses = fscache_requests = 0;
mem_pool_discard(cache->mem_pool, 0);
cache->mem_pool = NULL;
mem_pool_init(&cache->mem_pool, 0);
hashmap_free(&cache->map, 0);
hashmap_init(&cache->map, (hashmap_cmp_fn)fsentry_cmp, NULL, 0);
cache->lstat_requests = cache->opendir_requests = 0;
cache->fscache_misses = cache->fscache_requests = 0;
}
/*
* Checks if the cache is enabled for the given path.
*/
int fscache_enabled(const char *path)
static int do_fscache_enabled(struct fscache *cache, const char *path)
{
return enabled > 0 && !is_absolute_path(path);
return cache->enabled > 0 && !is_absolute_path(path);
}
/*
* Looks up a cache entry, waits if its being loaded by another thread.
* The mutex must be owned by the calling thread.
*/
static struct fsentry *fscache_get_wait(struct fsentry *key)
int fscache_enabled(const char *path)
{
struct fsentry *fse = hashmap_get(&map, key, NULL);
struct fscache *cache = fscache_getcache();
/* return if its a 'real' entry (future entries have refcnt == 0) */
if (!fse || fse->list || fse->refcnt)
return fse;
/* create an event and link our key to the future entry */
key->hwait = CreateEvent(NULL, TRUE, FALSE, NULL);
key->next = fse->next;
fse->next = key;
/* wait for the loading thread to signal us */
LeaveCriticalSection(&mutex);
WaitForSingleObject(key->hwait, INFINITE);
CloseHandle(key->hwait);
EnterCriticalSection(&mutex);
/* repeat cache lookup */
return hashmap_get(&map, key, NULL);
return cache ? do_fscache_enabled(cache, path) : 0;
}
/*
* Looks up or creates a cache entry for the specified key.
*/
static struct fsentry *fscache_get(struct fsentry *key)
static struct fsentry *fscache_get(struct fscache *cache, struct fsentry *key)
{
struct fsentry *fse, *future, *waiter;
struct fsentry *fse;
int dir_not_found;
EnterCriticalSection(&mutex);
fscache_requests++;
cache->fscache_requests++;
/* check if entry is in cache */
fse = fscache_get_wait(key);
fse = hashmap_get(&cache->map, key, NULL);
if (fse) {
if (fse->st_mode)
fsentry_addref(fse);
else
fse = NULL; /* non-existing directory */
LeaveCriticalSection(&mutex);
return fse;
}
/* if looking for a file, check if directory listing is in cache */
if (!fse && key->list) {
fse = fscache_get_wait(key->list);
fse = hashmap_get(&cache->map, key->list, NULL);
if (fse) {
LeaveCriticalSection(&mutex);
/*
* dir entry without file entry, or dir does not
* exist -> file doesn't exist
@@ -348,25 +331,8 @@ static struct fsentry *fscache_get(struct fsentry *key)
}
}
/* add future entry to indicate that we're loading it */
future = key->list ? key->list : key;
future->next = NULL;
future->refcnt = 0;
hashmap_add(&map, future);
/* create the directory listing (outside mutex!) */
LeaveCriticalSection(&mutex);
fse = fsentry_create_list(future, &dir_not_found);
EnterCriticalSection(&mutex);
/* remove future entry and signal waiting threads */
hashmap_remove(&map, future, NULL);
waiter = future->next;
while (waiter) {
HANDLE h = waiter->hwait;
waiter = waiter->next;
SetEvent(h);
}
/* create the directory listing */
fse = fsentry_create_list(cache, key->list ? key->list : key, &dir_not_found);
/* leave on error (errno set by fsentry_create_list) */
if (!fse) {
@@ -376,22 +342,21 @@ static struct fsentry *fscache_get(struct fsentry *key)
* empty, which for all practical matters is the same
* thing as far as fscache is concerned).
*/
fse = fsentry_alloc(key->list->list,
fse = fsentry_alloc(cache, key->list->list,
key->list->name, key->list->len);
fse->st_mode = 0;
hashmap_add(&map, fse);
hashmap_add(&cache->map, fse);
}
LeaveCriticalSection(&mutex);
return NULL;
}
/* add directory listing to the cache */
fscache_misses++;
fscache_add(fse);
cache->fscache_misses++;
fscache_add(cache, fse);
/* lookup file entry if requested (fse already points to directory) */
if (key->list)
fse = hashmap_get(&map, key, NULL);
fse = hashmap_get(&cache->map, key, NULL);
if (fse && !fse->st_mode)
fse = NULL; /* non-existing directory */
@@ -402,55 +367,104 @@ static struct fsentry *fscache_get(struct fsentry *key)
else
errno = ENOENT;
LeaveCriticalSection(&mutex);
return fse;
}
/*
* Enables or disables the cache. Note that the cache is read-only, changes to
* Enables the cache. Note that the cache is read-only, changes to
* the working directory are NOT reflected in the cache while enabled.
*/
int fscache_enable(int enable)
int fscache_enable(size_t initial_size)
{
int result;
int fscache;
struct fscache *cache;
int result = 0;
/* allow the cache to be disabled entirely */
fscache = git_env_bool("GIT_TEST_FSCACHE", -1);
if (fscache != -1)
core_fscache = fscache;
if (!core_fscache)
return 0;
/*
* refcount the global fscache initialization so that the
* opendir and lstat function pointers are redirected if
* any threads are using the fscache.
*/
if (!initialized) {
int fscache = git_env_bool("GIT_TEST_FSCACHE", -1);
/* allow the cache to be disabled entirely */
if (fscache != -1)
core_fscache = fscache;
if (!core_fscache)
return 0;
InitializeCriticalSection(&mutex);
lstat_requests = opendir_requests = 0;
fscache_misses = fscache_requests = 0;
hashmap_init(&map, (hashmap_cmp_fn) fsentry_cmp, NULL, 0);
initialized = 1;
}
if (!dwTlsIndex) {
dwTlsIndex = TlsAlloc();
if (dwTlsIndex == TLS_OUT_OF_INDEXES)
return 0;
}
result = enable ? InterlockedIncrement(&enabled)
: InterlockedDecrement(&enabled);
if (enable && result == 1) {
/* redirect opendir and lstat to the fscache implementations */
opendir = fscache_opendir;
lstat = fscache_lstat;
} else if (!enable && !result) {
}
InterlockedIncrement(&initialized);
/* refcount the thread specific initialization */
cache = fscache_getcache();
if (cache) {
InterlockedIncrement(&cache->enabled);
} else {
cache = (struct fscache *)xcalloc(1, sizeof(*cache));
cache->enabled = 1;
/*
* avoid having to rehash by leaving room for the parent dirs.
* '4' was determined empirically by testing several repos
*/
hashmap_init(&cache->map, (hashmap_cmp_fn)fsentry_cmp, NULL, initial_size * 4);
mem_pool_init(&cache->mem_pool, 0);
if (!TlsSetValue(dwTlsIndex, cache))
BUG("TlsSetValue error");
}
trace_printf_key(&trace_fscache, "fscache: enable\n");
return result;
}
/*
* Disables the cache.
*/
void fscache_disable(void)
{
struct fscache *cache;
if (!core_fscache)
return;
/* update the thread specific fscache initialization */
cache = fscache_getcache();
if (!cache)
BUG("fscache_disable() called on a thread where fscache has not been initialized");
if (!cache->enabled)
BUG("fscache_disable() called on an fscache that is already disabled");
InterlockedDecrement(&cache->enabled);
if (!cache->enabled) {
TlsSetValue(dwTlsIndex, NULL);
trace_printf_key(&trace_fscache, "fscache_disable: lstat %u, opendir %u, "
"total requests/misses %u/%u\n",
cache->lstat_requests, cache->opendir_requests,
cache->fscache_requests, cache->fscache_misses);
mem_pool_discard(cache->mem_pool, 0);
hashmap_free(&cache->map, 0);
free(cache);
}
/* update the global fscache initialization */
InterlockedDecrement(&initialized);
if (!initialized) {
/* reset opendir and lstat to the original implementations */
opendir = dirent_opendir;
lstat = mingw_lstat;
EnterCriticalSection(&mutex);
trace_printf_key(&trace_fscache, "fscache: lstat %u, opendir %u, "
"total requests/misses %u/%u\n",
lstat_requests, opendir_requests,
fscache_requests, fscache_misses);
fscache_clear();
LeaveCriticalSection(&mutex);
}
trace_printf_key(&trace_fscache, "fscache: enable(%d)\n", enable);
return result;
trace_printf_key(&trace_fscache, "fscache: disable\n");
return;
}
/*
@@ -458,10 +472,10 @@ int fscache_enable(int enable)
*/
void fscache_flush(void)
{
if (enabled) {
EnterCriticalSection(&mutex);
fscache_clear();
LeaveCriticalSection(&mutex);
struct fscache *cache = fscache_getcache();
if (cache && cache->enabled) {
fscache_clear(cache);
}
}
@@ -473,11 +487,12 @@ int fscache_lstat(const char *filename, struct stat *st)
{
int dirlen, base, len;
struct fsentry key[2], *fse;
struct fscache *cache = fscache_getcache();
if (!fscache_enabled(filename))
if (!cache || !do_fscache_enabled(cache, filename))
return mingw_lstat(filename, st);
lstat_requests++;
cache->lstat_requests++;
/* split filename into path + name */
len = strlen(filename);
if (len && is_dir_sep(filename[len - 1]))
@@ -490,7 +505,7 @@ int fscache_lstat(const char *filename, struct stat *st)
/* lookup entry for path + name in cache */
fsentry_init(key, NULL, filename, dirlen);
fsentry_init(key + 1, key, filename + base, len - base);
fse = fscache_get(key + 1);
fse = fscache_get(cache, key + 1);
if (!fse)
return -1;
@@ -554,11 +569,12 @@ DIR *fscache_opendir(const char *dirname)
struct fsentry key, *list;
fscache_DIR *dir;
int len;
struct fscache *cache = fscache_getcache();
if (!fscache_enabled(dirname))
if (!cache || !do_fscache_enabled(cache, dirname))
return dirent_opendir(dirname);
opendir_requests++;
cache->opendir_requests++;
/* prepare name (strip trailing '/', replace '.') */
len = strlen(dirname);
if ((len == 1 && dirname[0] == '.') ||
@@ -567,7 +583,7 @@ DIR *fscache_opendir(const char *dirname)
/* get directory listing from cache */
fsentry_init(&key, NULL, dirname, len);
list = fscache_get(&key);
list = fscache_get(cache, &key);
if (!list)
return NULL;
@@ -578,3 +594,55 @@ DIR *fscache_opendir(const char *dirname)
dir->pfsentry = list;
return (DIR*) dir;
}
struct fscache *fscache_getcache(void)
{
return (struct fscache *)TlsGetValue(dwTlsIndex);
}
void fscache_merge(struct fscache *dest)
{
struct hashmap_iter iter;
struct hashmap_entry *e;
struct fscache *cache = fscache_getcache();
/*
* Only do the merge if fscache was enabled and we have a dest
* cache to merge into.
*/
if (!dest) {
fscache_enable(0);
return;
}
if (!cache)
BUG("fscache_merge() called on a thread where fscache has not been initialized");
TlsSetValue(dwTlsIndex, NULL);
trace_printf_key(&trace_fscache, "fscache_merge: lstat %u, opendir %u, "
"total requests/misses %u/%u\n",
cache->lstat_requests, cache->opendir_requests,
cache->fscache_requests, cache->fscache_misses);
/*
* This is only safe because the primary thread we're merging into
* isn't being used so the critical section only needs to prevent
* the the child threads from stomping on each other.
*/
EnterCriticalSection(&mutex);
hashmap_iter_init(&cache->map, &iter);
while ((e = hashmap_iter_next(&iter)))
hashmap_add(&dest->map, e);
mem_pool_combine(dest->mem_pool, cache->mem_pool);
dest->lstat_requests += cache->lstat_requests;
dest->opendir_requests += cache->opendir_requests;
dest->fscache_requests += cache->fscache_requests;
dest->fscache_misses += cache->fscache_misses;
LeaveCriticalSection(&mutex);
free(cache);
InterlockedDecrement(&initialized);
}

View File

@@ -1,8 +1,16 @@
#ifndef FSCACHE_H
#define FSCACHE_H
int fscache_enable(int enable);
#define enable_fscache(x) fscache_enable(x)
/*
* The fscache is thread specific. enable_fscache() must be called
* for each thread where caching is desired.
*/
int fscache_enable(size_t initial_size);
#define enable_fscache(initial_size) fscache_enable(initial_size)
void fscache_disable(void);
#define disable_fscache() fscache_disable()
int fscache_enabled(const char *path);
#define is_fscache_enabled(path) fscache_enabled(path)
@@ -13,4 +21,13 @@ void fscache_flush(void);
DIR *fscache_opendir(const char *dir);
int fscache_lstat(const char *file_name, struct stat *buf);
/* opaque fscache structure */
struct fscache;
struct fscache *fscache_getcache(void);
#define getcache_fscache() fscache_getcache()
void fscache_merge(struct fscache *dest);
#define merge_fscache(dest) fscache_merge(dest)
#endif

View File

@@ -667,7 +667,7 @@ static void mark_complete_and_common_ref(struct fetch_negotiator *negotiator,
save_commit_buffer = 0;
enable_fscache(1);
enable_fscache(0);
for (ref = *refs; ref; ref = ref->next) {
struct object *o;
@@ -688,7 +688,7 @@ static void mark_complete_and_common_ref(struct fetch_negotiator *negotiator,
cutoff = commit->date;
}
}
enable_fscache(0);
disable_fscache();
if (!args->deepen) {
for_each_ref(mark_complete_oid, NULL);

View File

@@ -1283,10 +1283,18 @@ static inline int is_missing_file_error(int errno_)
* data or even file content without the need to synchronize with the file
* system.
*/
/* opaque fscache structure */
struct fscache;
#ifndef enable_fscache
#define enable_fscache(x) /* noop */
#endif
#ifndef disable_fscache
#define disable_fscache() /* noop */
#endif
#ifndef is_fscache_enabled
#define is_fscache_enabled(path) (0)
#endif
@@ -1295,6 +1303,14 @@ static inline int is_missing_file_error(int errno_)
#define flush_fscache() /* noop */
#endif
#ifndef getcache_fscache
#define getcache_fscache() (NULL) /* noop */
#endif
#ifndef merge_fscache
#define merge_fscache(dest) /* noop */
#endif
extern int cmd_main(int, const char **);
/*

View File

@@ -5,6 +5,7 @@
#include "cache.h"
#include "mem-pool.h"
static struct trace_key trace_mem_pool = TRACE_KEY_INIT(MEMPOOL);
#define BLOCK_GROWTH_SIZE 1024*1024 - sizeof(struct mp_block);
/*
@@ -48,12 +49,16 @@ void mem_pool_init(struct mem_pool **mem_pool, size_t initial_size)
mem_pool_alloc_block(pool, initial_size, NULL);
*mem_pool = pool;
trace_printf_key(&trace_mem_pool, "mem_pool (%p): init (%"PRIuMAX") initial size\n",
pool, (uintmax_t)initial_size);
}
void mem_pool_discard(struct mem_pool *mem_pool, int invalidate_memory)
{
struct mp_block *block, *block_to_free;
trace_printf_key(&trace_mem_pool, "mem_pool (%p): discard (%"PRIuMAX") unused\n",
mem_pool, (uintmax_t)(mem_pool->mp_block->end - mem_pool->mp_block->next_free));
block = mem_pool->mp_block;
while (block)
{

View File

@@ -10,6 +10,8 @@
#include "thread-utils.h"
#include "repository.h"
struct fscache *fscache;
/*
* Mostly randomly chosen maximum thread counts: we
* cap the parallelism to 20 threads, and we want
@@ -46,6 +48,7 @@ static void *preload_thread(void *_data)
nr = index->cache_nr - p->offset;
last_nr = nr;
enable_fscache(nr);
do {
struct cache_entry *ce = *cep++;
struct stat st;
@@ -88,6 +91,7 @@ static void *preload_thread(void *_data)
pthread_mutex_unlock(&pd->mutex);
}
cache_def_clear(&cache);
merge_fscache(fscache);
return NULL;
}
@@ -102,6 +106,7 @@ void preload_index(struct index_state *index,
if (!HAVE_THREADS || !core_preload_index)
return;
fscache = getcache_fscache();
threads = index->cache_nr / THREAD_COST;
if ((index->cache_nr > 1) && (threads < 2) && git_env_bool("GIT_TEST_PRELOAD_INDEX", 0))
threads = 2;
@@ -120,7 +125,6 @@ void preload_index(struct index_state *index,
pthread_mutex_init(&pd.mutex, NULL);
}
enable_fscache(1);
for (i = 0; i < threads; i++) {
struct thread_data *p = data+i;
int err;
@@ -146,7 +150,6 @@ void preload_index(struct index_state *index,
stop_progress(&pd.progress);
trace_performance_leave("preload index");
enable_fscache(0);
}
int repo_read_index_preload(struct repository *repo,

View File

@@ -1495,7 +1495,7 @@ int refresh_index(struct index_state *istate, unsigned int flags,
typechange_fmt = in_porcelain ? "T\t%s\n" : "%s: needs update\n";
added_fmt = in_porcelain ? "A\t%s\n" : "%s: needs update\n";
unmerged_fmt = in_porcelain ? "U\t%s\n" : "%s: needs merge\n";
enable_fscache(1);
enable_fscache(0);
/*
* Use the multi-threaded preload_index() to refresh most of the
* cache entries quickly then in the single threaded loop below,
@@ -1573,7 +1573,7 @@ int refresh_index(struct index_state *istate, unsigned int flags,
stop_progress(&progress);
}
trace_performance_leave("refresh index");
enable_fscache(0);
disable_fscache();
return has_errors;
}