Skip to content

Commit e81baea

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 b866146 commit e81baea

File tree

1 file changed

+30
-4
lines changed

1 file changed

+30
-4
lines changed

compat/win32/fscache.c

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,8 @@ static struct fsentry *fseentry_create_entry(struct fsentry *list,
165165
* Dir should not contain trailing '/'. Use an empty string for the current
166166
* directory (not "."!).
167167
*/
168-
static struct fsentry *fsentry_create_list(const struct fsentry *dir)
168+
static struct fsentry *fsentry_create_list(const struct fsentry *dir,
169+
int *dir_not_found)
169170
{
170171
wchar_t pattern[MAX_LONG_PATH + 2]; /* + 2 for "\*" */
171172
WIN32_FIND_DATAW fdata;
@@ -174,6 +175,8 @@ static struct fsentry *fsentry_create_list(const struct fsentry *dir)
174175
struct fsentry *list, **phead;
175176
DWORD err;
176177

178+
*dir_not_found = 0;
179+
177180
/* convert name to UTF-16 and check length */
178181
if ((wlen = xutftowcs_path_ex(pattern, dir->name, MAX_LONG_PATH,
179182
dir->len, MAX_PATH - 2, core_long_paths)) < 0)
@@ -192,6 +195,7 @@ static struct fsentry *fsentry_create_list(const struct fsentry *dir)
192195
h = FindFirstFileW(pattern, &fdata);
193196
if (h == INVALID_HANDLE_VALUE) {
194197
err = GetLastError();
198+
*dir_not_found = 1; /* or empty directory */
195199
errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(err);
196200
trace_printf_key(&trace_fscache, "fscache: error(%d) '%.*s'\n",
197201
errno, dir->len, dir->name);
@@ -200,6 +204,7 @@ static struct fsentry *fsentry_create_list(const struct fsentry *dir)
200204

201205
/* allocate object to hold directory listing */
202206
list = fsentry_alloc(NULL, dir->name, dir->len);
207+
list->st_mode = S_IFDIR;
203208

204209
/* walk directory and build linked list of fsentry structures */
205210
phead = &list->next;
@@ -284,12 +289,16 @@ static struct fsentry *fscache_get_wait(struct fsentry *key)
284289
static struct fsentry *fscache_get(struct fsentry *key)
285290
{
286291
struct fsentry *fse, *future, *waiter;
292+
int dir_not_found;
287293

288294
EnterCriticalSection(&mutex);
289295
/* check if entry is in cache */
290296
fse = fscache_get_wait(key);
291297
if (fse) {
292-
fsentry_addref(fse);
298+
if (fse->st_mode)
299+
fsentry_addref(fse);
300+
else
301+
fse = NULL; /* non-existing directory */
293302
LeaveCriticalSection(&mutex);
294303
return fse;
295304
}
@@ -298,7 +307,10 @@ static struct fsentry *fscache_get(struct fsentry *key)
298307
fse = fscache_get_wait(key->list);
299308
if (fse) {
300309
LeaveCriticalSection(&mutex);
301-
/* dir entry without file entry -> file doesn't exist */
310+
/*
311+
* dir entry without file entry, or dir does not
312+
* exist -> file doesn't exist
313+
*/
302314
errno = ENOENT;
303315
return NULL;
304316
}
@@ -312,7 +324,7 @@ static struct fsentry *fscache_get(struct fsentry *key)
312324

313325
/* create the directory listing (outside mutex!) */
314326
LeaveCriticalSection(&mutex);
315-
fse = fsentry_create_list(future);
327+
fse = fsentry_create_list(future, &dir_not_found);
316328
EnterCriticalSection(&mutex);
317329

318330
/* remove future entry and signal waiting threads */
@@ -326,6 +338,17 @@ static struct fsentry *fscache_get(struct fsentry *key)
326338

327339
/* leave on error (errno set by fsentry_create_list) */
328340
if (!fse) {
341+
if (dir_not_found && key->list) {
342+
/*
343+
* Record that the directory does not exist (or is
344+
* empty, which for all practical matters is the same
345+
* thing as far as fscache is concerned).
346+
*/
347+
fse = fsentry_alloc(key->list->list,
348+
key->list->name, key->list->len);
349+
fse->st_mode = 0;
350+
hashmap_add(&map, fse);
351+
}
329352
LeaveCriticalSection(&mutex);
330353
return NULL;
331354
}
@@ -337,6 +360,9 @@ static struct fsentry *fscache_get(struct fsentry *key)
337360
if (key->list)
338361
fse = hashmap_get(&map, key, NULL);
339362

363+
if (fse && !fse->st_mode)
364+
fse = NULL; /* non-existing directory */
365+
340366
/* return entry or ENOENT */
341367
if (fse)
342368
fsentry_addref(fse);

0 commit comments

Comments
 (0)