Skip to content

Commit 4d2b5b6

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 8d99b81 commit 4d2b5b6

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
@@ -181,7 +181,8 @@ static struct fsentry *fseentry_create_entry(struct fsentry *list,
181181
* Dir should not contain trailing '/'. Use an empty string for the current
182182
* directory (not "."!).
183183
*/
184-
static struct fsentry *fsentry_create_list(const struct fsentry *dir)
184+
static struct fsentry *fsentry_create_list(const struct fsentry *dir,
185+
int *dir_not_found)
185186
{
186187
wchar_t pattern[MAX_PATH + 2]; /* + 2 for '/' '*' */
187188
WIN32_FIND_DATAW fdata;
@@ -190,6 +191,8 @@ static struct fsentry *fsentry_create_list(const struct fsentry *dir)
190191
struct fsentry *list, **phead;
191192
DWORD err;
192193

194+
*dir_not_found = 0;
195+
193196
/* convert name to UTF-16 and check length < MAX_PATH */
194197
if ((wlen = xutftowcsn(pattern, dir->dirent.d_name, MAX_PATH,
195198
dir->len)) < 0) {
@@ -208,6 +211,7 @@ static struct fsentry *fsentry_create_list(const struct fsentry *dir)
208211
h = FindFirstFileW(pattern, &fdata);
209212
if (h == INVALID_HANDLE_VALUE) {
210213
err = GetLastError();
214+
*dir_not_found = 1; /* or empty directory */
211215
errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(err);
212216
trace_printf_key(&trace_fscache, "fscache: error(%d) '%s'\n",
213217
errno, dir->dirent.d_name);
@@ -216,6 +220,8 @@ static struct fsentry *fsentry_create_list(const struct fsentry *dir)
216220

217221
/* allocate object to hold directory listing */
218222
list = fsentry_alloc(NULL, dir->dirent.d_name, dir->len);
223+
list->st_mode = S_IFDIR;
224+
list->dirent.d_type = DT_DIR;
219225

220226
/* walk directory and build linked list of fsentry structures */
221227
phead = &list->next;
@@ -300,12 +306,16 @@ static struct fsentry *fscache_get_wait(struct fsentry *key)
300306
static struct fsentry *fscache_get(struct fsentry *key)
301307
{
302308
struct fsentry *fse, *future, *waiter;
309+
int dir_not_found;
303310

304311
EnterCriticalSection(&mutex);
305312
/* check if entry is in cache */
306313
fse = fscache_get_wait(key);
307314
if (fse) {
308-
fsentry_addref(fse);
315+
if (fse->st_mode)
316+
fsentry_addref(fse);
317+
else
318+
fse = NULL; /* non-existing directory */
309319
LeaveCriticalSection(&mutex);
310320
return fse;
311321
}
@@ -314,7 +324,10 @@ static struct fsentry *fscache_get(struct fsentry *key)
314324
fse = fscache_get_wait(key->list);
315325
if (fse) {
316326
LeaveCriticalSection(&mutex);
317-
/* dir entry without file entry -> file doesn't exist */
327+
/*
328+
* dir entry without file entry, or dir does not
329+
* exist -> file doesn't exist
330+
*/
318331
errno = ENOENT;
319332
return NULL;
320333
}
@@ -328,7 +341,7 @@ static struct fsentry *fscache_get(struct fsentry *key)
328341

329342
/* create the directory listing (outside mutex!) */
330343
LeaveCriticalSection(&mutex);
331-
fse = fsentry_create_list(future);
344+
fse = fsentry_create_list(future, &dir_not_found);
332345
EnterCriticalSection(&mutex);
333346

334347
/* remove future entry and signal waiting threads */
@@ -342,6 +355,18 @@ static struct fsentry *fscache_get(struct fsentry *key)
342355

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

381+
if (fse && !fse->st_mode)
382+
fse = NULL; /* non-existing directory */
383+
356384
/* return entry or ENOENT */
357385
if (fse)
358386
fsentry_addref(fse);

0 commit comments

Comments
 (0)