Skip to content

Commit f57dab8

Browse files
jeffhostetlerdscho
authored andcommitted
fscache: remember not-found directories
Teach FSCACHE to remember "not found" directories. This is a performance optimization. FSCACHE is a performance optimization available for Windows. It intercepts Posix-style lstat() calls into an in-memory directory using FindFirst/FindNext. It improves performance on Windows by catching the first lstat() call in a directory, using FindFirst/ FindNext to read the list of files (and attribute data) for the entire directory into the cache, and short-cut subsequent lstat() calls in the same directory. This gives a major performance boost on Windows. However, it does not remember "not found" directories. When STATUS runs and there are missing directories, the lstat() interception fails to find the parent directory and simply return ENOENT for the file -- it does not remember that the FindFirst on the directory failed. Thus subsequent lstat() calls in the same directory, each re-attempt the FindFirst. This completely defeats any performance gains. This can be seen by doing a sparse-checkout on a large repo and then doing a read-tree to reset the skip-worktree bits and then running status. This change reduced status times for my very large repo by 60%. Signed-off-by: Jeff Hostetler <[email protected]> Signed-off-by: Johannes Schindelin <[email protected]>
1 parent 8d16a41 commit f57dab8

File tree

1 file changed

+32
-4
lines changed

1 file changed

+32
-4
lines changed

compat/win32/fscache.c

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,8 @@ static struct fsentry *fseentry_create_entry(struct fsentry *list,
173173
* Dir should not contain trailing '/'. Use an empty string for the current
174174
* directory (not "."!).
175175
*/
176-
static struct fsentry *fsentry_create_list(const struct fsentry *dir)
176+
static struct fsentry *fsentry_create_list(const struct fsentry *dir,
177+
int *dir_not_found)
177178
{
178179
wchar_t pattern[MAX_PATH + 2]; /* + 2 for '/' '*' */
179180
WIN32_FIND_DATAW fdata;
@@ -182,6 +183,8 @@ static struct fsentry *fsentry_create_list(const struct fsentry *dir)
182183
struct fsentry *list, **phead;
183184
DWORD err;
184185

186+
*dir_not_found = 0;
187+
185188
/* convert name to UTF-16 and check length < MAX_PATH */
186189
if ((wlen = xutftowcsn(pattern, dir->dirent.d_name, MAX_PATH,
187190
dir->len)) < 0) {
@@ -200,6 +203,7 @@ static struct fsentry *fsentry_create_list(const struct fsentry *dir)
200203
h = FindFirstFileW(pattern, &fdata);
201204
if (h == INVALID_HANDLE_VALUE) {
202205
err = GetLastError();
206+
*dir_not_found = 1; /* or empty directory */
203207
errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(err);
204208
trace_printf_key(&trace_fscache, "fscache: error(%d) '%s'\n",
205209
errno, dir->dirent.d_name);
@@ -208,6 +212,8 @@ static struct fsentry *fsentry_create_list(const struct fsentry *dir)
208212

209213
/* allocate object to hold directory listing */
210214
list = fsentry_alloc(NULL, dir->dirent.d_name, dir->len);
215+
list->st_mode = S_IFDIR;
216+
list->dirent.d_type = DT_DIR;
211217

212218
/* walk directory and build linked list of fsentry structures */
213219
phead = &list->next;
@@ -292,12 +298,16 @@ static struct fsentry *fscache_get_wait(struct fsentry *key)
292298
static struct fsentry *fscache_get(struct fsentry *key)
293299
{
294300
struct fsentry *fse, *future, *waiter;
301+
int dir_not_found;
295302

296303
EnterCriticalSection(&mutex);
297304
/* check if entry is in cache */
298305
fse = fscache_get_wait(key);
299306
if (fse) {
300-
fsentry_addref(fse);
307+
if (fse->st_mode)
308+
fsentry_addref(fse);
309+
else
310+
fse = NULL; /* non-existing directory */
301311
LeaveCriticalSection(&mutex);
302312
return fse;
303313
}
@@ -306,7 +316,10 @@ static struct fsentry *fscache_get(struct fsentry *key)
306316
fse = fscache_get_wait(key->list);
307317
if (fse) {
308318
LeaveCriticalSection(&mutex);
309-
/* dir entry without file entry -> file doesn't exist */
319+
/*
320+
* dir entry without file entry, or dir does not
321+
* exist -> file doesn't exist
322+
*/
310323
errno = ENOENT;
311324
return NULL;
312325
}
@@ -320,7 +333,7 @@ static struct fsentry *fscache_get(struct fsentry *key)
320333

321334
/* create the directory listing (outside mutex!) */
322335
LeaveCriticalSection(&mutex);
323-
fse = fsentry_create_list(future);
336+
fse = fsentry_create_list(future, &dir_not_found);
324337
EnterCriticalSection(&mutex);
325338

326339
/* remove future entry and signal waiting threads */
@@ -334,6 +347,18 @@ static struct fsentry *fscache_get(struct fsentry *key)
334347

335348
/* leave on error (errno set by fsentry_create_list) */
336349
if (!fse) {
350+
if (dir_not_found && key->list) {
351+
/*
352+
* Record that the directory does not exist (or is
353+
* empty, which for all practical matters is the same
354+
* thing as far as fscache is concerned).
355+
*/
356+
fse = fsentry_alloc(key->list->list,
357+
key->list->dirent.d_name,
358+
key->list->len);
359+
fse->st_mode = 0;
360+
hashmap_add(&map, &fse->ent);
361+
}
337362
LeaveCriticalSection(&mutex);
338363
return NULL;
339364
}
@@ -345,6 +370,9 @@ static struct fsentry *fscache_get(struct fsentry *key)
345370
if (key->list)
346371
fse = hashmap_get_entry(&map, key, ent, NULL);
347372

373+
if (fse && !fse->st_mode)
374+
fse = NULL; /* non-existing directory */
375+
348376
/* return entry or ENOENT */
349377
if (fse)
350378
fsentry_addref(fse);

0 commit comments

Comments
 (0)