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 <frekui@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Fredrik Kuivinen committed Aug 21, 2011 at 00:42 UTC 9eceddeec6ccdbcb0c6ce02a45357ffd5a428f39
3 files changed +49 -23
grep.c
+45 -21
@@ -137,16 +137,50 @@ static void free_pcre_regexp(struct grep_pat *p)
137 }
138 #endif /* !USE_LIBPCRE */
139
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 +
158 static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
159 {
160 int err;
161
162 p->word_regexp = opt->word_regexp;
163 p->ignore_case = opt->ignore_case;
146 - p->fixed = opt->fixed;
164
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);
182 return;
183 + }
184
185 if (opt->pcre) {
186 compile_pcre_regexp(p, opt);
@@ -395,7 +429,9 @@ void free_grep_patterns(struct grep_opt *opt)
429 case GREP_PATTERN: /* atom */
430 case GREP_PATTERN_HEAD:
431 case GREP_PATTERN_BODY:
398 - if (p->pcre_regexp)
432 + if (p->kws)
433 + kwsfree(p->kws);
434 + else if (p->pcre_regexp)
435 free_pcre_regexp(p);
436 else
437 regfree(&p->regexp);
@@ -455,26 +491,14 @@ static void show_name(struct grep_opt *opt, const char *name)
491 static int fixmatch(struct grep_pat *p, char *line, char *eol,
492 regmatch_t *match)
493 {
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) {
497 match->rm_so = match->rm_eo = -1;
498 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];
502 return 0;
503 }
504 }
grep.h
+2
@@ -7,6 +7,7 @@
7 typedef int pcre;
8 typedef int pcre_extra;
9 #endif
10 +#include "kwset.h"
11
12 enum grep_pat_token {
13 GREP_PATTERN,
@@ -41,6 +42,7 @@ struct grep_pat {
42 regex_t regexp;
43 pcre *pcre_regexp;
44 pcre_extra *pcre_extra_info;
45 + kwset_t kws;
46 unsigned fixed:1;
47 unsigned ignore_case:1;
48 unsigned word_regexp:1;
t/t7008-grep-binary.sh
+2 -2
@@ -84,7 +84,7 @@ test_expect_success 'git grep -Fi Y<NUL>f a' "
84 git grep -f f -Fi a
85 "
86
87 -test_expect_failure 'git grep -Fi Y<NUL>x a' "
87 +test_expect_success 'git grep -Fi Y<NUL>x a' "
88 printf 'YQx' | q_to_nul >f &&
89 test_must_fail git grep -f f -Fi a
90 "
@@ -94,7 +94,7 @@ test_expect_success 'git grep y<NUL>f a' "
94 git grep -f f a
95 "
96
97 -test_expect_failure 'git grep y<NUL>x a' "
97 +test_expect_success 'git grep y<NUL>x a' "
98 printf 'yQx' | q_to_nul >f &&
99 test_must_fail git grep -f f a
100 "