Skip to content

Commit c12f366

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 ce58ce2 commit c12f366

File tree

1 file changed

+32
-4
lines changed

1 file changed

+32
-4
lines changed

compat/win32/fscache.c

+32-4
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,8 @@ static struct fsentry *fseentry_create_entry(struct fsentry *list,
179179
* Dir should not contain trailing '/'. Use an empty string for the current
180180
* directory (not "."!).
181181
*/
182-
static struct fsentry *fsentry_create_list(const struct fsentry *dir)
182+
static struct fsentry *fsentry_create_list(const struct fsentry *dir,
183+
int *dir_not_found)
183184
{
184185
wchar_t pattern[MAX_PATH + 2]; /* + 2 for '/' '*' */
185186
WIN32_FIND_DATAW fdata;
@@ -188,6 +189,8 @@ static struct fsentry *fsentry_create_list(const struct fsentry *dir)
188189
struct fsentry *list, **phead;
189190
DWORD err;
190191

192+
*dir_not_found = 0;
193+
191194
/* convert name to UTF-16 and check length < MAX_PATH */
192195
if ((wlen = xutftowcsn(pattern, dir->dirent.d_name, MAX_PATH,
193196
dir->len)) < 0) {
@@ -206,6 +209,7 @@ static struct fsentry *fsentry_create_list(const struct fsentry *dir)
206209
h = FindFirstFileW(pattern, &fdata);
207210
if (h == INVALID_HANDLE_VALUE) {
208211
err = GetLastError();
212+
*dir_not_found = 1; /* or empty directory */
209213
errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(err);
210214
trace_printf_key(&trace_fscache, "fscache: error(%d) '%s'\n",
211215
errno, dir->dirent.d_name);
@@ -214,6 +218,8 @@ static struct fsentry *fsentry_create_list(const struct fsentry *dir)
214218

215219
/* allocate object to hold directory listing */
216220
list = fsentry_alloc(NULL, dir->dirent.d_name, dir->len);
221+
list->st_mode = S_IFDIR;
222+
list->dirent.d_type = DT_DIR;
217223

218224
/* walk directory and build linked list of fsentry structures */
219225
phead = &list->next;
@@ -298,12 +304,16 @@ static struct fsentry *fscache_get_wait(struct fsentry *key)
298304
static struct fsentry *fscache_get(struct fsentry *key)
299305
{
300306
struct fsentry *fse, *future, *waiter;
307+
int dir_not_found;
301308

302309
EnterCriticalSection(&mutex);
303310
/* check if entry is in cache */
304311
fse = fscache_get_wait(key);
305312
if (fse) {
306-
fsentry_addref(fse);
313+
if (fse->st_mode)
314+
fsentry_addref(fse);
315+
else
316+
fse = NULL; /* non-existing directory */
307317
LeaveCriticalSection(&mutex);
308318
return fse;
309319
}
@@ -312,7 +322,10 @@ static struct fsentry *fscache_get(struct fsentry *key)
312322
fse = fscache_get_wait(key->list);
313323
if (fse) {
314324
LeaveCriticalSection(&mutex);
315-
/* dir entry without file entry -> file doesn't exist */
325+
/*
326+
* dir entry without file entry, or dir does not
327+
* exist -> file doesn't exist
328+
*/
316329
errno = ENOENT;
317330
return NULL;
318331
}
@@ -326,7 +339,7 @@ static struct fsentry *fscache_get(struct fsentry *key)
326339

327340
/* create the directory listing (outside mutex!) */
328341
LeaveCriticalSection(&mutex);
329-
fse = fsentry_create_list(future);
342+
fse = fsentry_create_list(future, &dir_not_found);
330343
EnterCriticalSection(&mutex);
331344

332345
/* remove future entry and signal waiting threads */
@@ -340,6 +353,18 @@ static struct fsentry *fscache_get(struct fsentry *key)
340353

341354
/* leave on error (errno set by fsentry_create_list) */
342355
if (!fse) {
356+
if (dir_not_found && key->list) {
357+
/*
358+
* Record that the directory does not exist (or is
359+
* empty, which for all practical matters is the same
360+
* thing as far as fscache is concerned).
361+
*/
362+
fse = fsentry_alloc(key->list->list,
363+
key->list->dirent.d_name,
364+
key->list->len);
365+
fse->st_mode = 0;
366+
hashmap_add(&map, &fse->ent);
367+
}
343368
LeaveCriticalSection(&mutex);
344369
return NULL;
345370
}
@@ -351,6 +376,9 @@ static struct fsentry *fscache_get(struct fsentry *key)
351376
if (key->list)
352377
fse = hashmap_get_entry(&map, key, ent, NULL);
353378

379+
if (fse && !fse->st_mode)
380+
fse = NULL; /* non-existing directory */
381+
354382
/* return entry or ENOENT */
355383
if (fse)
356384
fsentry_addref(fse);

0 commit comments

Comments
 (0)