@matheusbsbs / matheus-bagliano / commits / e16c60d9f9
Fork 0

MinGW readdir reimplementation to support d_type

The original readdir implementation was fast, but didn't support the d_type. This means that git would do additional lstats for each entry, to figure out if the entry was a directory or not. This unneedingly slowed down many operations, since Windows API provides this information directly when walking the directories. By running this implementation on Moe's repo structure: mkdir bummer && cd bummer; for ((i=0;i<100;i++)); do mkdir $i && pushd $i; for ((j=0;j<1000;j++)); do echo "$j" >$j; done; popd; done We see the following speedups: git add . ------------------- old: 00:00:23(.087) new: 00:00:21(.512) 1.07x git status ------------------- old: 00:00:03(.306) new: 00:00:01(.684) 1.96x git clean -dxf ------------------- old: 00:00:01(.918) new: 00:00:00(.295) 6.50x Signed-off-by: Marius Storm-Olsen <marius@trolltech.com> Signed-off-by: Steffen Prohaska <prohaska@zib.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Marius Storm-Olsen committed May 31, 2009 at 18:15 UTC e16c60d9f98c66fd2c9bba64562b3439970c13f8
2 files changed +88
compat/mingw.c
+59
@@ -1172,3 +1172,62 @@ char *getpass(const char *prompt)
1172 fputs("\n", stderr);
1173 return strbuf_detach(&buf, NULL);
1174 }
1175 +
1176 +#ifndef NO_MINGW_REPLACE_READDIR
1177 +/* MinGW readdir implementation to avoid extra lstats for Git */
1178 +struct mingw_DIR
1179 +{
1180 + struct _finddata_t dd_dta; /* disk transfer area for this dir */
1181 + struct mingw_dirent dd_dir; /* Our own implementation, including d_type */
1182 + long dd_handle; /* _findnext handle */
1183 + int dd_stat; /* 0 = next entry to read is first entry, -1 = off the end, positive = 0 based index of next entry */
1184 + char dd_name[1]; /* given path for dir with search pattern (struct is extended) */
1185 +};
1186 +
1187 +struct dirent *mingw_readdir(DIR *dir)
1188 +{
1189 + WIN32_FIND_DATAA buf;
1190 + HANDLE handle;
1191 + struct mingw_DIR *mdir = (struct mingw_DIR*)dir;
1192 +
1193 + if (!dir->dd_handle) {
1194 + errno = EBADF; /* No set_errno for mingw */
1195 + return NULL;
1196 + }
1197 +
1198 + if (dir->dd_handle == (long)INVALID_HANDLE_VALUE && dir->dd_stat == 0)
1199 + {
1200 + handle = FindFirstFileA(dir->dd_name, &buf);
1201 + DWORD lasterr = GetLastError();
1202 + dir->dd_handle = (long)handle;
1203 + if (handle == INVALID_HANDLE_VALUE && (lasterr != ERROR_NO_MORE_FILES)) {
1204 + errno = err_win_to_posix(lasterr);
1205 + return NULL;
1206 + }
1207 + } else if (dir->dd_handle == (long)INVALID_HANDLE_VALUE) {
1208 + return NULL;
1209 + } else if (!FindNextFileA((HANDLE)dir->dd_handle, &buf)) {
1210 + DWORD lasterr = GetLastError();
1211 + FindClose((HANDLE)dir->dd_handle);
1212 + dir->dd_handle = (long)INVALID_HANDLE_VALUE;
1213 + /* POSIX says you shouldn't set errno when readdir can't
1214 + find any more files; so, if another error we leave it set. */
1215 + if (lasterr != ERROR_NO_MORE_FILES)
1216 + errno = err_win_to_posix(lasterr);
1217 + return NULL;
1218 + }
1219 +
1220 + /* We get here if `buf' contains valid data. */
1221 + strcpy(dir->dd_dir.d_name, buf.cFileName);
1222 + ++dir->dd_stat;
1223 +
1224 + /* Set file type, based on WIN32_FIND_DATA */
1225 + mdir->dd_dir.d_type = 0;
1226 + if (buf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1227 + mdir->dd_dir.d_type |= DT_DIR;
1228 + else
1229 + mdir->dd_dir.d_type |= DT_REG;
1230 +
1231 + return (struct dirent*)&dir->dd_dir;
1232 +}
1233 +#endif // !NO_MINGW_REPLACE_READDIR
compat/mingw.h
+29
@@ -235,3 +235,32 @@ int main(int argc, const char **argv) \
235 return mingw_main(argc, argv); \
236 } \
237 static int mingw_main(c,v)
238 +
239 +#ifndef NO_MINGW_REPLACE_READDIR
240 +/*
241 + * A replacement of readdir, to ensure that it reads the file type at
242 + * the same time. This avoid extra unneeded lstats in git on MinGW
243 + */
244 +#undef DT_UNKNOWN
245 +#undef DT_DIR
246 +#undef DT_REG
247 +#undef DT_LNK
248 +#define DT_UNKNOWN 0
249 +#define DT_DIR 1
250 +#define DT_REG 2
251 +#define DT_LNK 3
252 +
253 +struct mingw_dirent
254 +{
255 + long d_ino; /* Always zero. */
256 + union {
257 + unsigned short d_reclen; /* Always zero. */
258 + unsigned char d_type; /* Reimplementation adds this */
259 + };
260 + unsigned short d_namlen; /* Length of name in d_name. */
261 + char d_name[FILENAME_MAX]; /* File name. */
262 +};
263 +#define dirent mingw_dirent
264 +#define readdir(x) mingw_readdir(x)
265 +struct dirent *mingw_readdir(DIR *dir);
266 +#endif // !NO_MINGW_REPLACE_READDIR