From: Lars Knoll <lars@trolltech.com>
To: Junio C Hamano <gitster@pobox.com>
Cc: "Morten Welinder" <mwelinder@gmail.com>,
git@vger.kernel.org, "Pierre Habouzit" <madcoder@debian.org>
Subject: [PATCH v3] Speedup scanning for excluded files.
Date: Tue, 30 Oct 2007 09:46:01 +0200 [thread overview]
Message-ID: <200710300846.02010.lars@trolltech.com> (raw)
In-Reply-To: <200710300828.49840.lars@trolltech.com>
Try to avoid a lot of work scanning for excluded files,
by caching some more information when setting up the exclusion
data structure.
Speeds up 'git runstatus' on a repository containing the Qt sources by 30% and
reduces the amount of instructions executed (as measured by valgrind) by a
factor of 2. A 'git runstatus' on the git repository goes from 100M instructions
down to about 22M.
Signed-off-by: Lars Knoll <lars@trolltech.com>
---
Included an out of bounds check for the issue Morten found.
dir.c | 61 ++++++++++++++++++++++++++++++++++++++++++++-----------------
dir.h | 7 +++++++
2 files changed, 51 insertions(+), 17 deletions(-)
diff --git a/dir.c b/dir.c
index 4c17d36..b3d7462 100644
--- a/dir.c
+++ b/dir.c
@@ -118,14 +118,32 @@ int match_pathspec(const char **pathspec, const char *name, int namelen, int pre
return retval;
}
+static int no_wildcard(const char *string)
+{
+ return string[strcspn(string, "*?[{")] == '\0';
+}
+
void add_exclude(const char *string, const char *base,
int baselen, struct exclude_list *which)
{
struct exclude *x = xmalloc(sizeof (*x));
+ x->to_exclude = 1;
+ if (*string == '!') {
+ x->to_exclude = 0;
+ string++;
+ }
x->pattern = string;
+ x->patternlen = strlen(string);
x->base = base;
x->baselen = baselen;
+ x->flags = 0;
+ if (!strchr(string, '/'))
+ x->flags |= EXC_FLAG_NODIR;
+ if (no_wildcard(string))
+ x->flags |= EXC_FLAG_NOWILDCARD;
+ if (*string == '*' && no_wildcard(string+1))
+ x->flags |= EXC_FLAG_ENDSWITH;
if (which->nr == which->alloc) {
which->alloc = alloc_nr(which->alloc);
which->excludes = xrealloc(which->excludes,
@@ -209,7 +227,7 @@ void pop_exclude_per_directory(struct dir_struct *dir, int stk)
* Return 1 for exclude, 0 for include and -1 for undecided.
*/
static int excluded_1(const char *pathname,
- int pathlen,
+ int pathlen, const char *basename,
struct exclude_list *el)
{
int i;
@@ -218,19 +236,21 @@ static int excluded_1(const char *pathname,
for (i = el->nr - 1; 0 <= i; i--) {
struct exclude *x = el->excludes[i];
const char *exclude = x->pattern;
- int to_exclude = 1;
+ int to_exclude = x->to_exclude;
- if (*exclude == '!') {
- to_exclude = 0;
- exclude++;
- }
-
- if (!strchr(exclude, '/')) {
+ if (x->flags & EXC_FLAG_NODIR) {
/* match basename */
- const char *basename = strrchr(pathname, '/');
- basename = (basename) ? basename+1 : pathname;
- if (fnmatch(exclude, basename, 0) == 0)
- return to_exclude;
+ if (x->flags & EXC_FLAG_NOWILDCARD) {
+ if (!strcmp(exclude, basename))
+ return to_exclude;
+ } else if (x->flags & EXC_FLAG_ENDSWITH) {
+ if (x->patternlen - 1 <= pathlen &&
+ !strcmp(exclude + 1, pathname + pathlen - x->patternlen + 1))
+ return to_exclude;
+ } else {
+ if (fnmatch(exclude, basename, 0) == 0)
+ return to_exclude;
+ }
}
else {
/* match with FNM_PATHNAME:
@@ -246,9 +266,14 @@ static int excluded_1(const char *pathname,
strncmp(pathname, x->base, baselen))
continue;
- if (fnmatch(exclude, pathname+baselen,
- FNM_PATHNAME) == 0)
- return to_exclude;
+ if (x->flags & EXC_FLAG_NOWILDCARD) {
+ if (!strcmp(exclude, pathname + baselen))
+ return to_exclude;
+ } else {
+ if (fnmatch(exclude, pathname+baselen,
+ FNM_PATHNAME) == 0)
+ return to_exclude;
+ }
}
}
}
@@ -259,9 +284,11 @@ int excluded(struct dir_struct *dir, const char *pathname)
{
int pathlen = strlen(pathname);
int st;
-
+ const char *basename = strrchr(pathname, '/');
+ basename = (basename) ? basename+1 : pathname;
+
for (st = EXC_CMDL; st <= EXC_FILE; st++) {
- switch (excluded_1(pathname, pathlen, &dir->exclude_list[st])) {
+ switch (excluded_1(pathname, pathlen, basename, &dir->exclude_list[st])) {
case 0:
return 0;
case 1:
diff --git a/dir.h b/dir.h
index a248a23..3ce8dbe 100644
--- a/dir.h
+++ b/dir.h
@@ -17,13 +17,20 @@ struct dir_entry {
char name[FLEX_ARRAY]; /* more */
};
+#define EXC_FLAG_NODIR 1
+#define EXC_FLAG_NOWILDCARD 2
+#define EXC_FLAG_ENDSWITH 4
+
struct exclude_list {
int nr;
int alloc;
struct exclude {
const char *pattern;
+ int patternlen;
const char *base;
int baselen;
+ int to_exclude;
+ int flags;
} **excludes;
};
--
1.5.3.4.383.gd90a7
prev parent reply other threads:[~2007-10-30 7:46 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2007-10-29 7:45 [PATCH] Speedup scanning for excluded files Lars Knoll
2007-10-29 8:02 ` Pierre Habouzit
2007-10-29 8:59 ` Lars Knoll
2007-10-29 22:17 ` Junio C Hamano
2007-10-29 22:59 ` Morten Welinder
2007-10-30 0:00 ` Junio C Hamano
2007-10-30 7:28 ` Lars Knoll
2007-10-30 7:46 ` Lars Knoll [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=200710300846.02010.lars@trolltech.com \
--to=lars@trolltech.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=madcoder@debian.org \
--cc=mwelinder@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).