Skip to content

Commit 9ecedde

Browse files
frekuigitster
authored andcommitted
Use kwset in grep
Benchmarks for the hot cache case: before: $ perf stat --repeat=5 git grep qwerty > /dev/null Performance counter stats for 'git grep qwerty' (5 runs): 3,478,085 cache-misses # 2.322 M/sec ( +- 2.690% ) 11,356,177 cache-references # 7.582 M/sec ( +- 2.598% ) 3,872,184 branch-misses # 0.363 % ( +- 0.258% ) 1,067,367,848 branches # 712.673 M/sec ( +- 2.622% ) 3,828,370,782 instructions # 0.947 IPC ( +- 0.033% ) 4,043,832,831 cycles # 2700.037 M/sec ( +- 0.167% ) 8,518 page-faults # 0.006 M/sec ( +- 3.648% ) 847 CPU-migrations # 0.001 M/sec ( +- 3.262% ) 6,546 context-switches # 0.004 M/sec ( +- 2.292% ) 1497.695495 task-clock-msecs # 3.303 CPUs ( +- 2.550% ) 0.453394396 seconds time elapsed ( +- 0.912% ) after: $ perf stat --repeat=5 git grep qwerty > /dev/null Performance counter stats for 'git grep qwerty' (5 runs): 2,989,918 cache-misses # 3.166 M/sec ( +- 5.013% ) 10,986,041 cache-references # 11.633 M/sec ( +- 4.899% ) (scaled from 95.06%) 3,511,993 branch-misses # 1.422 % ( +- 0.785% ) 246,893,561 branches # 261.433 M/sec ( +- 3.967% ) 1,392,727,757 instructions # 0.564 IPC ( +- 0.040% ) 2,468,142,397 cycles # 2613.494 M/sec ( +- 0.110% ) 7,747 page-faults # 0.008 M/sec ( +- 3.995% ) 897 CPU-migrations # 0.001 M/sec ( +- 2.383% ) 6,535 context-switches # 0.007 M/sec ( +- 1.993% ) 944.384228 task-clock-msecs # 3.177 CPUs ( +- 0.268% ) 0.297257643 seconds time elapsed ( +- 0.450% ) So we gain about 35% by using the kwset code. As a side effect of using kwset two grep tests are fixed by this patch. The first is fixed because kwset can deal with case-insensitive search containing NULs, something strcasestr cannot do. The second one is fixed because we consider patterns containing NULs as fixed strings (regcomp cannot accept patterns with NULs). Signed-off-by: Fredrik Kuivinen <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent b95c5ad commit 9ecedde

File tree

3 files changed

+49
-23
lines changed

3 files changed

+49
-23
lines changed

grep.c

Lines changed: 45 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -137,16 +137,50 @@ static void free_pcre_regexp(struct grep_pat *p)
137137
}
138138
#endif /* !USE_LIBPCRE */
139139

140+
static int is_fixed(const char *s, size_t len)
141+
{
142+
size_t i;
143+
144+
/* regcomp cannot accept patterns with NULs so we
145+
* consider any pattern containing a NUL fixed.
146+
*/
147+
if (memchr(s, 0, len))
148+
return 1;
149+
150+
for (i = 0; i < len; i++) {
151+
if (is_regex_special(s[i]))
152+
return 0;
153+
}
154+
155+
return 1;
156+
}
157+
140158
static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
141159
{
142160
int err;
143161

144162
p->word_regexp = opt->word_regexp;
145163
p->ignore_case = opt->ignore_case;
146-
p->fixed = opt->fixed;
147164

148-
if (p->fixed)
165+
if (opt->fixed || is_fixed(p->pattern, p->patternlen))
166+
p->fixed = 1;
167+
else
168+
p->fixed = 0;
169+
170+
if (p->fixed) {
171+
if (opt->regflags & REG_ICASE || p->ignore_case) {
172+
static char trans[256];
173+
int i;
174+
for (i = 0; i < 256; i++)
175+
trans[i] = tolower(i);
176+
p->kws = kwsalloc(trans);
177+
} else {
178+
p->kws = kwsalloc(NULL);
179+
}
180+
kwsincr(p->kws, p->pattern, p->patternlen);
181+
kwsprep(p->kws);
149182
return;
183+
}
150184

151185
if (opt->pcre) {
152186
compile_pcre_regexp(p, opt);
@@ -395,7 +429,9 @@ void free_grep_patterns(struct grep_opt *opt)
395429
case GREP_PATTERN: /* atom */
396430
case GREP_PATTERN_HEAD:
397431
case GREP_PATTERN_BODY:
398-
if (p->pcre_regexp)
432+
if (p->kws)
433+
kwsfree(p->kws);
434+
else if (p->pcre_regexp)
399435
free_pcre_regexp(p);
400436
else
401437
regfree(&p->regexp);
@@ -455,26 +491,14 @@ static void show_name(struct grep_opt *opt, const char *name)
455491
static int fixmatch(struct grep_pat *p, char *line, char *eol,
456492
regmatch_t *match)
457493
{
458-
char *hit;
459-
460-
if (p->ignore_case) {
461-
char *s = line;
462-
do {
463-
hit = strcasestr(s, p->pattern);
464-
if (hit)
465-
break;
466-
s += strlen(s) + 1;
467-
} while (s < eol);
468-
} else
469-
hit = memmem(line, eol - line, p->pattern, p->patternlen);
470-
471-
if (!hit) {
494+
struct kwsmatch kwsm;
495+
size_t offset = kwsexec(p->kws, line, eol - line, &kwsm);
496+
if (offset == -1) {
472497
match->rm_so = match->rm_eo = -1;
473498
return REG_NOMATCH;
474-
}
475-
else {
476-
match->rm_so = hit - line;
477-
match->rm_eo = match->rm_so + p->patternlen;
499+
} else {
500+
match->rm_so = offset;
501+
match->rm_eo = match->rm_so + kwsm.size[0];
478502
return 0;
479503
}
480504
}

grep.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
typedef int pcre;
88
typedef int pcre_extra;
99
#endif
10+
#include "kwset.h"
1011

1112
enum grep_pat_token {
1213
GREP_PATTERN,
@@ -41,6 +42,7 @@ struct grep_pat {
4142
regex_t regexp;
4243
pcre *pcre_regexp;
4344
pcre_extra *pcre_extra_info;
45+
kwset_t kws;
4446
unsigned fixed:1;
4547
unsigned ignore_case:1;
4648
unsigned word_regexp:1;

t/t7008-grep-binary.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ test_expect_success 'git grep -Fi Y<NUL>f a' "
8484
git grep -f f -Fi a
8585
"
8686

87-
test_expect_failure 'git grep -Fi Y<NUL>x a' "
87+
test_expect_success 'git grep -Fi Y<NUL>x a' "
8888
printf 'YQx' | q_to_nul >f &&
8989
test_must_fail git grep -f f -Fi a
9090
"
@@ -94,7 +94,7 @@ test_expect_success 'git grep y<NUL>f a' "
9494
git grep -f f a
9595
"
9696

97-
test_expect_failure 'git grep y<NUL>x a' "
97+
test_expect_success 'git grep y<NUL>x a' "
9898
printf 'yQx' | q_to_nul >f &&
9999
test_must_fail git grep -f f a
100100
"

0 commit comments

Comments
 (0)