Skip to content

Commit 5e81234

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 <[email protected]> Signed-off-by: Johannes Schindelin <[email protected]>
1 parent 8d02a68 commit 5e81234

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
@@ -186,7 +186,8 @@ static struct fsentry *fseentry_create_entry(struct fsentry *list,
186186
* Dir should not contain trailing '/'. Use an empty string for the current
187187
* directory (not "."!).
188188
*/
189-
static struct fsentry *fsentry_create_list(const struct fsentry *dir)
189+
static struct fsentry *fsentry_create_list(const struct fsentry *dir,
190+
int *dir_not_found)
190191
{
191192
wchar_t pattern[MAX_PATH + 2]; /* + 2 for '/' '*' */
192193
WIN32_FIND_DATAW fdata;
@@ -195,6 +196,8 @@ static struct fsentry *fsentry_create_list(const struct fsentry *dir)
195196
struct fsentry *list, **phead;
196197
DWORD err;
197198

199+
*dir_not_found = 0;
200+
198201
/* convert name to UTF-16 and check length < MAX_PATH */
199202
if ((wlen = xutftowcsn(pattern, dir->dirent.d_name, MAX_PATH,
200203
dir->len)) < 0) {
@@ -213,6 +216,7 @@ static struct fsentry *fsentry_create_list(const struct fsentry *dir)
213216
h = FindFirstFileW(pattern, &fdata);
214217
if (h == INVALID_HANDLE_VALUE) {
215218
err = GetLastError();
219+
*dir_not_found = 1; /* or empty directory */
216220
errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(err);
217221
trace_printf_key(&trace_fscache, "fscache: error(%d) '%s'\n",
218222
errno, dir->dirent.d_name);
@@ -221,6 +225,8 @@ static struct fsentry *fsentry_create_list(const struct fsentry *dir)
221225

222226
/* allocate object to hold directory listing */
223227
list = fsentry_alloc(NULL, dir->dirent.d_name, dir->len);
228+
list->st_mode = S_IFDIR;
229+
list->dirent.d_type = DT_DIR;
224230

225231
/* walk directory and build linked list of fsentry structures */
226232
phead = &list->next;
@@ -305,12 +311,16 @@ static struct fsentry *fscache_get_wait(struct fsentry *key)
305311
static struct fsentry *fscache_get(struct fsentry *key)
306312
{
307313
struct fsentry *fse, *future, *waiter;
314+
int dir_not_found;
308315

309316
EnterCriticalSection(&mutex);
310317
/* check if entry is in cache */
311318
fse = fscache_get_wait(key);
312319
if (fse) {
313-
fsentry_addref(fse);
320+
if (fse->st_mode)
321+
fsentry_addref(fse);
322+
else
323+
fse = NULL; /* non-existing directory */
314324
LeaveCriticalSection(&mutex);
315325
return fse;
316326
}
@@ -319,7 +329,10 @@ static struct fsentry *fscache_get(struct fsentry *key)
319329
fse = fscache_get_wait(key->list);
320330
if (fse) {
321331
LeaveCriticalSection(&mutex);
322-
/* dir entry without file entry -> file doesn't exist */
332+
/*
333+
* dir entry without file entry, or dir does not
334+
* exist -> file doesn't exist
335+
*/
323336
errno = ENOENT;
324337
return NULL;
325338
}
@@ -333,7 +346,7 @@ static struct fsentry *fscache_get(struct fsentry *key)
333346

334347
/* create the directory listing (outside mutex!) */
335348
LeaveCriticalSection(&mutex);
336-
fse = fsentry_create_list(future);
349+
fse = fsentry_create_list(future, &dir_not_found);
337350
EnterCriticalSection(&mutex);
338351

339352
/* remove future entry and signal waiting threads */
@@ -347,6 +360,18 @@ static struct fsentry *fscache_get(struct fsentry *key)
347360

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

386+
if (fse && !fse->st_mode)
387+
fse = NULL; /* non-existing directory */
388+
361389
/* return entry or ENOENT */
362390
if (fse)
363391
fsentry_addref(fse);

0 commit comments

Comments
 (0)