Skip to content

Commit 99bce2d

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 43d266b commit 99bce2d

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_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 < MAX_PATH */
178181
if ((wlen = xutftowcsn(pattern, dir->name, MAX_PATH, dir->len)) < 0) {
179182
if (errno == ERANGE)
@@ -191,6 +194,7 @@ static struct fsentry *fsentry_create_list(const struct fsentry *dir)
191194
h = FindFirstFileW(pattern, &fdata);
192195
if (h == INVALID_HANDLE_VALUE) {
193196
err = GetLastError();
197+
*dir_not_found = 1; /* or empty directory */
194198
errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(err);
195199
trace_printf_key(&trace_fscache, "fscache: error(%d) '%.*s'\n",
196200
errno, dir->len, dir->name);
@@ -199,6 +203,7 @@ static struct fsentry *fsentry_create_list(const struct fsentry *dir)
199203

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

203208
/* walk directory and build linked list of fsentry structures */
204209
phead = &list->next;
@@ -283,12 +288,16 @@ static struct fsentry *fscache_get_wait(struct fsentry *key)
283288
static struct fsentry *fscache_get(struct fsentry *key)
284289
{
285290
struct fsentry *fse, *future, *waiter;
291+
int dir_not_found;
286292

287293
EnterCriticalSection(&mutex);
288294
/* check if entry is in cache */
289295
fse = fscache_get_wait(key);
290296
if (fse) {
291-
fsentry_addref(fse);
297+
if (fse->st_mode)
298+
fsentry_addref(fse);
299+
else
300+
fse = NULL; /* non-existing directory */
292301
LeaveCriticalSection(&mutex);
293302
return fse;
294303
}
@@ -297,7 +306,10 @@ static struct fsentry *fscache_get(struct fsentry *key)
297306
fse = fscache_get_wait(key->list);
298307
if (fse) {
299308
LeaveCriticalSection(&mutex);
300-
/* dir entry without file entry -> file doesn't exist */
309+
/*
310+
* dir entry without file entry, or dir does not
311+
* exist -> file doesn't exist
312+
*/
301313
errno = ENOENT;
302314
return NULL;
303315
}
@@ -311,7 +323,7 @@ static struct fsentry *fscache_get(struct fsentry *key)
311323

312324
/* create the directory listing (outside mutex!) */
313325
LeaveCriticalSection(&mutex);
314-
fse = fsentry_create_list(future);
326+
fse = fsentry_create_list(future, &dir_not_found);
315327
EnterCriticalSection(&mutex);
316328

317329
/* remove future entry and signal waiting threads */
@@ -325,6 +337,17 @@ static struct fsentry *fscache_get(struct fsentry *key)
325337

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

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

0 commit comments

Comments
 (0)