Skip to content

Commit

Permalink
Win32: add a cache below mingw's lstat and readdir implementations
Browse files Browse the repository at this point in the history
Checking the work tree status is quite slow on Windows, due to slow lstat
emulation (git calls lstat once for each file in the index). Windows
operating system APIs seem to be much better at scanning the status
of entire directories than checking single files.

Add an lstat implementation that uses a cache for lstat data. Cache misses
read the entire parent directory and add it to the cache. Subsequent lstat
calls for the same directory are served directly from the cache.

Also implement opendir / readdir / closedir so that they create and use
directory listings in the cache.

The cache doesn't track file system changes and doesn't plug into any
modifying file APIs, so it has to be explicitly enabled for git functions
that don't modify the working copy. For a start, enable the cache for the
entire 'git status' command and the core.preloadIndex feature.

The cache can be disabled at runtime by setting environment variable
GIT_NOFSCACHE, or at compile time by unsetting USE_FSCACHE in the Makefile.

Note: in an earlier version of this patch, the cache was always active and
tracked file system changes via ReadDirectoryChangesW. However, this was
much more complex and had negative impact on the performance of modifying
git commands such as 'git checkout'.

Signed-off-by: Karsten Blees <blees@dcon.de>
  • Loading branch information
kblees committed Oct 24, 2012
1 parent f7eb85c commit 35f3196
Show file tree
Hide file tree
Showing 10 changed files with 570 additions and 18 deletions.
6 changes: 4 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -1286,8 +1286,9 @@ ifeq ($(uname_S),Windows)
BASIC_CFLAGS = -nologo -I. -I../zlib -Icompat/vcbuild -Icompat/vcbuild/include -DWIN32 -D_CONSOLE -DHAVE_STRING_H -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE
COMPAT_OBJS = compat/msvc.o compat/winansi.o \
compat/win32/pthread.o compat/win32/syslog.o \
compat/win32/dirent.o
compat/win32/dirent.o compat/win32/fscache.o
COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -DHAVE_ALLOCA_H -Icompat -Icompat/regex -Icompat/win32 -DSTRIP_EXTENSION=\".exe\"
COMPAT_CFLAGS += -DUSE_FSCACHE -DNO_GETTEXT
BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO -SUBSYSTEM:CONSOLE
EXTLIBS = user32.lib advapi32.lib shell32.lib wininet.lib ws2_32.lib
PTHREAD_LIBS =
Expand Down Expand Up @@ -1427,11 +1428,12 @@ ifneq (,$(findstring MINGW,$(uname_S)))
NO_POSIX_GOODIES = UnfortunatelyYes
DEFAULT_HELP_FORMAT = html
NO_D_INO_IN_DIRENT = YesPlease
COMPAT_CFLAGS += -DUSE_FSCACHE
COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/win32
COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
COMPAT_OBJS += compat/mingw.o compat/winansi.o \
compat/win32/pthread.o compat/win32/syslog.o \
compat/win32/dirent.o
compat/win32/dirent.o compat/win32/fscache.o
BASIC_LDFLAGS += -Wl,--large-address-aware
EXTLIBS += -lws2_32
GITLIBS += git.res
Expand Down
3 changes: 3 additions & 0 deletions builtin/commit.c
Original file line number Diff line number Diff line change
Expand Up @@ -1195,6 +1195,9 @@ int cmd_status(int argc, const char **argv, const char *prefix)
if (*argv)
s.pathspec = get_pathspec(prefix, argv);

#ifdef USE_FSCACHE
fscache_enable(1);
#endif
read_cache_preload(s.pathspec);
refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, s.pathspec, NULL, NULL);

Expand Down
19 changes: 3 additions & 16 deletions compat/mingw.c
Original file line number Diff line number Diff line change
Expand Up @@ -440,22 +440,6 @@ int mingw_chmod(const char *filename, int mode)
return _wchmod(wfilename, mode);
}

/*
* The unit of FILETIME is 100-nanoseconds since January 1, 1601, UTC.
* Returns the 100-nanoseconds ("hekto nanoseconds") since the epoch.
*/
static inline long long filetime_to_hnsec(const FILETIME *ft)
{
long long winTime = ((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime;
/* Windows to Unix Epoch conversion */
return winTime - 116444736000000000LL;
}

static inline time_t filetime_to_time_t(const FILETIME *ft)
{
return (time_t)(filetime_to_hnsec(ft) / 10000000);
}

/* We keep the do_lstat code in a separate function to avoid recursion.
* When a path ends with a slash, the stat will fail with ENOENT. In
* this case, we strip the trailing slashes and stat again.
Expand Down Expand Up @@ -2129,6 +2113,9 @@ void mingw_startup()
_setmode(_fileno(stdout), _O_BINARY);
_setmode(_fileno(stderr), _O_BINARY);

/* initialize fscache */
fscache_init();

/* initialize Unicode console */
winansi_init();
}
16 changes: 16 additions & 0 deletions compat/mingw.h
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,22 @@ static inline int getrlimit(int resource, struct rlimit *rlp)
return 0;
}

/*
* The unit of FILETIME is 100-nanoseconds since January 1, 1601, UTC.
* Returns the 100-nanoseconds ("hekto nanoseconds") since the epoch.
*/
static inline long long filetime_to_hnsec(const FILETIME *ft)
{
long long winTime = ((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime;
/* Windows to Unix Epoch conversion */
return winTime - 116444736000000000LL;
}

static inline time_t filetime_to_time_t(const FILETIME *ft)
{
return (time_t)(filetime_to_hnsec(ft) / 10000000);
}

/* Use mingw_lstat() instead of lstat()/stat() and
* mingw_fstat() instead of fstat() on Windows.
*/
Expand Down
3 changes: 3 additions & 0 deletions compat/win32/dirent.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "../../git-compat-util.h"

#ifndef USE_FSCACHE

struct DIR {
struct dirent dd_dir; /* includes d_type */
HANDLE dd_handle; /* FindFirstFile handle */
Expand Down Expand Up @@ -90,3 +92,4 @@ int closedir(DIR *dir)
free(dir);
return 0;
}
#endif
3 changes: 3 additions & 0 deletions compat/win32/dirent.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#ifndef DIRENT_H
#define DIRENT_H

#ifndef USE_FSCACHE

typedef struct DIR DIR;

#define DT_UNKNOWN 0
Expand All @@ -17,4 +19,5 @@ DIR *opendir(const char *dirname);
struct dirent *readdir(DIR *dir);
int closedir(DIR *dir);

#endif /* USE_FSCACHE */
#endif /* DIRENT_H */
Loading

0 comments on commit 35f3196

Please sign in to comment.