Skip to content

Commit f378e02

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 2d7d611 commit f378e02

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
@@ -178,7 +178,8 @@ static struct fsentry *fseentry_create_entry(struct fsentry *list,
178178
* Dir should not contain trailing '/'. Use an empty string for the current
179179
* directory (not "."!).
180180
*/
181-
static struct fsentry *fsentry_create_list(const struct fsentry *dir)
181+
static struct fsentry *fsentry_create_list(const struct fsentry *dir,
182+
int *dir_not_found)
182183
{
183184
wchar_t pattern[MAX_PATH + 2]; /* + 2 for '/' '*' */
184185
WIN32_FIND_DATAW fdata;
@@ -187,6 +188,8 @@ static struct fsentry *fsentry_create_list(const struct fsentry *dir)
187188
struct fsentry *list, **phead;
188189
DWORD err;
189190

191+
*dir_not_found = 0;
192+
190193
/* convert name to UTF-16 and check length < MAX_PATH */
191194
if ((wlen = xutftowcsn(pattern, dir->dirent.d_name, MAX_PATH,
192195
dir->len)) < 0) {
@@ -205,6 +208,7 @@ static struct fsentry *fsentry_create_list(const struct fsentry *dir)
205208
h = FindFirstFileW(pattern, &fdata);
206209
if (h == INVALID_HANDLE_VALUE) {
207210
err = GetLastError();
211+
*dir_not_found = 1; /* or empty directory */
208212
errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(err);
209213
trace_printf_key(&trace_fscache, "fscache: error(%d) '%s'\n",
210214
errno, dir->dirent.d_name);
@@ -213,6 +217,8 @@ static struct fsentry *fsentry_create_list(const struct fsentry *dir)
213217

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

217223
/* walk directory and build linked list of fsentry structures */
218224
phead = &list->next;
@@ -297,12 +303,16 @@ static struct fsentry *fscache_get_wait(struct fsentry *key)
297303
static struct fsentry *fscache_get(struct fsentry *key)
298304
{
299305
struct fsentry *fse, *future, *waiter;
306+
int dir_not_found;
300307

301308
EnterCriticalSection(&mutex);
302309
/* check if entry is in cache */
303310
fse = fscache_get_wait(key);
304311
if (fse) {
305-
fsentry_addref(fse);
312+
if (fse->st_mode)
313+
fsentry_addref(fse);
314+
else
315+
fse = NULL; /* non-existing directory */
306316
LeaveCriticalSection(&mutex);
307317
return fse;
308318
}
@@ -311,7 +321,10 @@ static struct fsentry *fscache_get(struct fsentry *key)
311321
fse = fscache_get_wait(key->list);
312322
if (fse) {
313323
LeaveCriticalSection(&mutex);
314-
/* dir entry without file entry -> file doesn't exist */
324+
/*
325+
* dir entry without file entry, or dir does not
326+
* exist -> file doesn't exist
327+
*/
315328
errno = ENOENT;
316329
return NULL;
317330
}
@@ -325,7 +338,7 @@ static struct fsentry *fscache_get(struct fsentry *key)
325338

326339
/* create the directory listing (outside mutex!) */
327340
LeaveCriticalSection(&mutex);
328-
fse = fsentry_create_list(future);
341+
fse = fsentry_create_list(future, &dir_not_found);
329342
EnterCriticalSection(&mutex);
330343

331344
/* remove future entry and signal waiting threads */
@@ -339,6 +352,18 @@ static struct fsentry *fscache_get(struct fsentry *key)
339352

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

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

0 commit comments

Comments
 (0)