Skip to content

Commit bf25acc

Browse files
jeffhostetlerGit for Windows Build Agent
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 <jeffhost@microsoft.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
1 parent 8ee31d2 commit bf25acc

1 file changed

Lines changed: 32 additions & 4 deletions

File tree

compat/win32/fscache.c

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,8 @@ static struct fsentry *fseentry_create_entry(struct fsentry *list,
188188
* Dir should not contain trailing '/'. Use an empty string for the current
189189
* directory (not "."!).
190190
*/
191-
static struct fsentry *fsentry_create_list(const struct fsentry *dir)
191+
static struct fsentry *fsentry_create_list(const struct fsentry *dir,
192+
int *dir_not_found)
192193
{
193194
wchar_t pattern[MAX_PATH + 2]; /* + 2 for '/' '*' */
194195
WIN32_FIND_DATAW fdata;
@@ -197,6 +198,8 @@ static struct fsentry *fsentry_create_list(const struct fsentry *dir)
197198
struct fsentry *list, **phead;
198199
DWORD err;
199200

201+
*dir_not_found = 0;
202+
200203
/* convert name to UTF-16 and check length < MAX_PATH */
201204
if ((wlen = xutftowcsn(pattern, dir->dirent.d_name, MAX_PATH,
202205
dir->len)) < 0) {
@@ -215,6 +218,7 @@ static struct fsentry *fsentry_create_list(const struct fsentry *dir)
215218
h = FindFirstFileW(pattern, &fdata);
216219
if (h == INVALID_HANDLE_VALUE) {
217220
err = GetLastError();
221+
*dir_not_found = 1; /* or empty directory */
218222
errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(err);
219223
trace_printf_key(&trace_fscache, "fscache: error(%d) '%s'\n",
220224
errno, dir->dirent.d_name);
@@ -223,6 +227,8 @@ static struct fsentry *fsentry_create_list(const struct fsentry *dir)
223227

224228
/* allocate object to hold directory listing */
225229
list = fsentry_alloc(NULL, dir->dirent.d_name, dir->len);
230+
list->st_mode = S_IFDIR;
231+
list->dirent.d_type = DT_DIR;
226232

227233
/* walk directory and build linked list of fsentry structures */
228234
phead = &list->next;
@@ -307,12 +313,16 @@ static struct fsentry *fscache_get_wait(struct fsentry *key)
307313
static struct fsentry *fscache_get(struct fsentry *key)
308314
{
309315
struct fsentry *fse, *future, *waiter;
316+
int dir_not_found;
310317

311318
EnterCriticalSection(&mutex);
312319
/* check if entry is in cache */
313320
fse = fscache_get_wait(key);
314321
if (fse) {
315-
fsentry_addref(fse);
322+
if (fse->st_mode)
323+
fsentry_addref(fse);
324+
else
325+
fse = NULL; /* non-existing directory */
316326
LeaveCriticalSection(&mutex);
317327
return fse;
318328
}
@@ -321,7 +331,10 @@ static struct fsentry *fscache_get(struct fsentry *key)
321331
fse = fscache_get_wait(key->list);
322332
if (fse) {
323333
LeaveCriticalSection(&mutex);
324-
/* dir entry without file entry -> file doesn't exist */
334+
/*
335+
* dir entry without file entry, or dir does not
336+
* exist -> file doesn't exist
337+
*/
325338
errno = ENOENT;
326339
return NULL;
327340
}
@@ -335,7 +348,7 @@ static struct fsentry *fscache_get(struct fsentry *key)
335348

336349
/* create the directory listing (outside mutex!) */
337350
LeaveCriticalSection(&mutex);
338-
fse = fsentry_create_list(future);
351+
fse = fsentry_create_list(future, &dir_not_found);
339352
EnterCriticalSection(&mutex);
340353

341354
/* remove future entry and signal waiting threads */
@@ -349,6 +362,18 @@ static struct fsentry *fscache_get(struct fsentry *key)
349362

350363
/* leave on error (errno set by fsentry_create_list) */
351364
if (!fse) {
365+
if (dir_not_found && key->list) {
366+
/*
367+
* Record that the directory does not exist (or is
368+
* empty, which for all practical matters is the same
369+
* thing as far as fscache is concerned).
370+
*/
371+
fse = fsentry_alloc(key->list->list,
372+
key->list->dirent.d_name,
373+
key->list->len);
374+
fse->st_mode = 0;
375+
hashmap_add(&map, &fse->ent);
376+
}
352377
LeaveCriticalSection(&mutex);
353378
return NULL;
354379
}
@@ -360,6 +385,9 @@ static struct fsentry *fscache_get(struct fsentry *key)
360385
if (key->list)
361386
fse = hashmap_get_entry(&map, key, ent, NULL);
362387

388+
if (fse && !fse->st_mode)
389+
fse = NULL; /* non-existing directory */
390+
363391
/* return entry or ENOENT */
364392
if (fse)
365393
fsentry_addref(fse);

0 commit comments

Comments
 (0)