Subject: [PATCH v3 01/13] dir.c: add MEASURE_EXCLUDE code for tracking exclude performance

From: Nguyễn Thái Ngọc Duy <[email-masked]>
Date: Tue, 12 Mar 2013 20:04:48 +0700

Hot read_directory() codepaths are tracked. This could be helpful to
see how changes affect read_directory() performance.

Results are printed when environment variable GIT_MEASURE_EXCLUDE is set.

Signed-off-by: Nguyễn Thái Ngọc Duy <[email-masked]>
---
 dir.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 100 insertions(+), 9 deletions(-)

diff --git a/dir.c b/dir.c
index 57394e4..69c045b 100644
--- a/dir.c
+++ b/dir.c
@@ -12,6 +12,32 @@
 #include "refs.h"
 #include "wildmatch.h"
 
+#ifdef MEASURE_EXCLUDE
+static uint32_t tv_treat_leading_path;
+static uint32_t tv_read_directory;
+static uint32_t tv_treat_one_path;
+static uint32_t tv_is_excluded;
+static uint32_t tv_prep_exclude;
+static uint32_t tv_last_exclude_matching;
+static uint32_t tv_dir_add_name;
+static uint32_t tv_directory_exists_in_index;
+static uint32_t tv_simplify_away;
+static uint32_t tv_index_name_exists;
+static uint32_t tv_lazy_init_name_hash;
+#define START_CLOCK() \
+	{ \
+		struct timeval tv1, tv2; \
+		gettimeofday(&tv1, NULL);
+#define STOP_CLOCK(v) \
+		gettimeofday(&tv2, NULL); \
+		v += (uint64_t)tv2.tv_sec * 1000000 + tv2.tv_usec - \
+			(uint64_t)tv1.tv_sec * 1000000 - tv1.tv_usec; \
+	}
+#else
+#define START_CLOCK()
+#define STOP_CLOCK(v)
+#endif
+
 struct path_simplify {
 	int len;
 	const char *path;
@@ -768,8 +794,11 @@ static struct exclude *last_exclude_matching(struct dir_struct *dir,
 	const char *basename = strrchr(pathname, '/');
 	basename = (basename) ? basename+1 : pathname;
 
+	START_CLOCK();
 	prep_exclude(dir, pathname, basename-pathname);
+	STOP_CLOCK(tv_prep_exclude);
 
+	START_CLOCK();
 	for (i = EXC_CMDL; i <= EXC_FILE; i++) {
 		group = &dir->exclude_list_group[i];
 		for (j = group->nr - 1; j >= 0; j--) {
@@ -780,6 +809,7 @@ static struct exclude *last_exclude_matching(struct dir_struct *dir,
 				return exclude;
 		}
 	}
+	STOP_CLOCK(tv_last_exclude_matching);
 	return NULL;
 }
 
@@ -897,9 +927,14 @@ static struct dir_entry *dir_entry_new(const char *pathname, int len)
 
 static struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len)
 {
-	if (!(dir->flags & DIR_SHOW_IGNORED) &&
-	    cache_name_exists(pathname, len, ignore_case))
-		return NULL;
+	if (!(dir->flags & DIR_SHOW_IGNORED)) {
+		struct cache_entry *ce;
+		START_CLOCK();
+		ce = cache_name_exists(pathname, len, ignore_case);
+		STOP_CLOCK(tv_index_name_exists);
+		if (ce)
+			return NULL;
+	}
 
 	ALLOC_GROW(dir->entries, dir->nr+1, dir->alloc);
 	return dir->entries[dir->nr++] = dir_entry_new(pathname, len);
@@ -1034,8 +1069,12 @@ static enum directory_treatment treat_directory(struct dir_struct *dir,
 	const char *dirname, int len, int exclude,
 	const struct path_simplify *simplify)
 {
+	int ret;
+	START_CLOCK();
 	/* The "len-1" is to strip the final '/' */
-	switch (directory_exists_in_index(dirname, len-1)) {
+	ret = directory_exists_in_index(dirname, len-1);
+	STOP_CLOCK(tv_directory_exists_in_index);
+	switch (ret) {
 	case index_directory:
 		if ((dir->flags & DIR_SHOW_OTHER_DIRECTORIES) && exclude)
 			break;
@@ -1179,7 +1218,9 @@ static int get_index_dtype(const char *path, int len)
 	int pos;
 	struct cache_entry *ce;
 
+	START_CLOCK();
 	ce = cache_name_exists(path, len, 0);
+	STOP_CLOCK(tv_index_name_exists);
 	if (ce) {
 		if (!ce_uptodate(ce))
 			return DT_UNKNOWN;
@@ -1244,7 +1285,12 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,
 					  const struct path_simplify *simplify,
 					  int dtype, struct dirent *de)
 {
-	int exclude = is_excluded(dir, path->buf, &dtype);
+	int exclude;
+
+	START_CLOCK();
+	exclude = is_excluded(dir, path->buf,  &dtype);
+	STOP_CLOCK(tv_is_excluded);
+
 	if (exclude && (dir->flags & DIR_COLLECT_IGNORED)
 	    && exclude_matches_pathspec(path->buf, path->len, simplify))
 		dir_add_ignored(dir, path->buf, path->len);
@@ -1292,17 +1338,23 @@ static enum path_treatment treat_path(struct dir_struct *dir,
 				      int baselen,
 				      const struct path_simplify *simplify)
 {
-	int dtype;
+	int dtype, ret;
 
 	if (is_dot_or_dotdot(de->d_name) || !strcmp(de->d_name, ".git"))
 		return path_ignored;
 	strbuf_setlen(path, baselen);
 	strbuf_addstr(path, de->d_name);
-	if (simplify_away(path->buf, path->len, simplify))
+	START_CLOCK();
+	ret = simplify_away(path->buf, path->len, simplify);
+	STOP_CLOCK(tv_simplify_away);
+	if (ret)
 		return path_ignored;
 
 	dtype = DTYPE(de);
-	return treat_one_path(dir, path, simplify, dtype, de);
+	START_CLOCK();
+	ret = treat_one_path(dir, path, simplify, dtype, de);
+	STOP_CLOCK(tv_treat_one_path);
+	return ret;
 }
 
 /*
@@ -1345,7 +1397,9 @@ static int read_directory_recursive(struct dir_struct *dir,
 		contents++;
 		if (check_only)
 			break;
+		START_CLOCK();
 		dir_add_name(dir, path.buf, path.len);
+		STOP_CLOCK(tv_dir_add_name);
 	}
 	closedir(fdir);
  out:
@@ -1405,6 +1459,7 @@ static int treat_leading_path(struct dir_struct *dir,
 		len--;
 	if (!len)
 		return 1;
+	START_CLOCK();
 	baselen = 0;
 	while (1) {
 		cp = path + baselen + !!baselen;
@@ -1428,6 +1483,7 @@ static int treat_leading_path(struct dir_struct *dir,
 		}
 	}
 	strbuf_release(&sb);
+	STOP_CLOCK(tv_treat_leading_path);
 	return rc;
 }
 
@@ -1439,8 +1495,43 @@ int read_directory(struct dir_struct *dir, const char *path, int len, const char
 		return dir->nr;
 
 	simplify = create_simplify(pathspec);
-	if (!len || treat_leading_path(dir, path, len, simplify))
+	if (!len || treat_leading_path(dir, path, len, simplify)) {
+#ifdef MEASURE_EXCLUDE
+		/* The first call triggers lazy_init_name_hash() */
+		START_CLOCK();
+		index_name_exists(&the_index, "", 0, ignore_case);
+		STOP_CLOCK(tv_lazy_init_name_hash);
+#endif
+		START_CLOCK();
 		read_directory_recursive(dir, path, len, 0, simplify);
+		STOP_CLOCK(tv_read_directory);
+	}
+#ifdef MEASURE_EXCLUDE
+	if (getenv("GIT_MEASURE_EXCLUDE")) {
+		fprintf(stderr, "treat_leading_path:  %10.3f\n",
+			(float)tv_treat_leading_path / 1000000);
+		fprintf(stderr, "read_directory:      %10.3f\n",
+			(float)tv_read_directory / 1000000);
+		fprintf(stderr, "+treat_one_path:     %10.3f\n",
+			(float)tv_treat_one_path / 1000000);
+		fprintf(stderr, "++is_excluded:       %10.3f\n",
+			(float)tv_is_excluded / 1000000);
+		fprintf(stderr, "+++prep_exclude:     %10.3f\n",
+			(float)tv_prep_exclude / 1000000);
+		fprintf(stderr, "+++matching:         %10.3f\n",
+			(float)tv_last_exclude_matching / 1000000);
+		fprintf(stderr, "++dir_exist:         %10.3f\n",
+			(float)tv_directory_exists_in_index / 1000000);
+		fprintf(stderr, "++index_name_exists: %10.3f\n",
+			(float)tv_index_name_exists / 1000000);
+		fprintf(stderr, "lazy_init_name_hash: %10.3f\n",
+			(float)tv_lazy_init_name_hash / 1000000);
+		fprintf(stderr, "+simplify_away:      %10.3f\n",
+			(float)tv_simplify_away / 1000000);
+		fprintf(stderr, "+dir_add_name:       %10.3f\n",
+			(float)tv_dir_add_name / 1000000);
+	}
+#endif
 	free_simplify(simplify);
 	qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
 	qsort(dir->ignored, dir->ignored_nr, sizeof(struct dir_entry *), cmp_name);
-- 
1.8.1.2.536.gf441e6d