Skip to content

Commit fbaceaa

Browse files
avargitster
authored andcommitted
grep: add support for the PCRE v1 JIT API
Change the grep PCRE v1 code to use JIT when available. When PCRE support was initially added in commit 63e7e9d ("git-grep: Learn PCRE", 2011-05-09) PCRE had no JIT support, it was integrated into 8.20 released on 2011-10-21. Enabling JIT support usually improves performance by more than 40%. The pattern compilation times are relatively slower, but those relative numbers are tiny, and are easily made back in all but the most trivial cases of grep. Detailed benchmarks & overview of compilation times is at: http://sljit.sourceforge.net/pcre.html With this change the difference in a t/perf/p7820-grep-engines.sh run is, with just the /perl/ tests shown: $ GIT_PERF_REPEAT_COUNT=30 GIT_PERF_LARGE_REPO=~/g/linux GIT_PERF_MAKE_OPTS='-j8 USE_LIBPCRE=YesPlease CC=~/perl5/installed/bin/gcc NO_R_TO_GCC_LINKER=YesPlease CFLAGS=-O3 LIBPCREDIR=/home/avar/g/pcre/inst LDFLAGS=-Wl,-rpath,/home/avar/g/pcre/inst/lib' ./run HEAD~ HEAD p7820-grep-engines.sh Test HEAD~ HEAD --------------------------------------------------------------------------------------- 7820.3: perl grep 'how.to' 0.35(1.11+0.43) 0.23(0.42+0.46) -34.3% 7820.7: perl grep '^how to' 0.64(2.71+0.36) 0.27(0.66+0.44) -57.8% 7820.11: perl grep '[how] to' 0.63(2.51+0.42) 0.33(0.98+0.39) -47.6% 7820.15: perl grep '(e.t[^ ]*|v.ry) rare' 1.17(5.61+0.35) 0.34(1.08+0.46) -70.9% 7820.19: perl grep 'm(ú|u)lt.b(æ|y)te' 0.43(1.52+0.44) 0.30(0.88+0.42) -30.2% The conditional support for JIT is implemented as suggested in the pcrejit(3) man page. E.g. defining PCRE_STUDY_JIT_COMPILE to 0 if it's not present. The implementation is relatively verbose because even if PCRE_CONFIG_JIT is defined only a call to pcre_config() can determine if the JIT is available, and if so the faster pcre_jit_exec() function should be called instead of pcre_exec(), and a different (but not complimentary!) function needs to be called to free pcre1_extra_info. There's no graceful fallback if pcre_jit_stack_alloc() fails under PCRE_CONFIG_JIT, instead the program will simply abort. I don't think this is worth handling gracefully, it'll only fail in cases where malloc() doesn't work, in which case we're screwed anyway. That there's no assignment of `p->pcre1_jit_on = 0` when PCRE_CONFIG_JIT isn't defined isn't a bug. The create_grep_pat() function allocates the grep_pat allocates it with calloc(), so it's guaranteed to be 0 when PCRE_CONFIG_JIT isn't defined. I you're bisecting and find this change, check that your PCRE isn't older than 8.32. This change intentionally broke really old versions of PCRE, but that's fixed in follow-up commits. Signed-off-by: Ævar Arnfjörð Bjarmason <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 7531a2d commit fbaceaa

File tree

2 files changed

+42
-4
lines changed

2 files changed

+42
-4
lines changed

grep.c

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -365,9 +365,22 @@ static void compile_pcre1_regexp(struct grep_pat *p, const struct grep_opt *opt)
365365
if (!p->pcre1_regexp)
366366
compile_regexp_failed(p, error);
367367

368-
p->pcre1_extra_info = pcre_study(p->pcre1_regexp, 0, &error);
368+
p->pcre1_extra_info = pcre_study(p->pcre1_regexp, PCRE_STUDY_JIT_COMPILE, &error);
369369
if (!p->pcre1_extra_info && error)
370370
die("%s", error);
371+
372+
#ifdef PCRE_CONFIG_JIT
373+
pcre_config(PCRE_CONFIG_JIT, &p->pcre1_jit_on);
374+
if (p->pcre1_jit_on == 1) {
375+
p->pcre1_jit_stack = pcre_jit_stack_alloc(1, 1024 * 1024);
376+
if (!p->pcre1_jit_stack)
377+
die("Couldn't allocate PCRE JIT stack");
378+
pcre_assign_jit_stack(p->pcre1_extra_info, NULL, p->pcre1_jit_stack);
379+
} else if (p->pcre1_jit_on != 0) {
380+
die("BUG: The pcre1_jit_on variable should be 0 or 1, not %d",
381+
p->pcre1_jit_on);
382+
}
383+
#endif
371384
}
372385

373386
static int pcre1match(struct grep_pat *p, const char *line, const char *eol,
@@ -378,8 +391,19 @@ static int pcre1match(struct grep_pat *p, const char *line, const char *eol,
378391
if (eflags & REG_NOTBOL)
379392
flags |= PCRE_NOTBOL;
380393

381-
ret = pcre_exec(p->pcre1_regexp, p->pcre1_extra_info, line, eol - line,
382-
0, flags, ovector, ARRAY_SIZE(ovector));
394+
#ifdef PCRE_CONFIG_JIT
395+
if (p->pcre1_jit_on) {
396+
ret = pcre_jit_exec(p->pcre1_regexp, p->pcre1_extra_info, line,
397+
eol - line, 0, flags, ovector,
398+
ARRAY_SIZE(ovector), p->pcre1_jit_stack);
399+
} else
400+
#endif
401+
{
402+
ret = pcre_exec(p->pcre1_regexp, p->pcre1_extra_info, line,
403+
eol - line, 0, flags, ovector,
404+
ARRAY_SIZE(ovector));
405+
}
406+
383407
if (ret < 0 && ret != PCRE_ERROR_NOMATCH)
384408
die("pcre_exec failed with error code %d", ret);
385409
if (ret > 0) {
@@ -394,7 +418,15 @@ static int pcre1match(struct grep_pat *p, const char *line, const char *eol,
394418
static void free_pcre1_regexp(struct grep_pat *p)
395419
{
396420
pcre_free(p->pcre1_regexp);
397-
pcre_free(p->pcre1_extra_info);
421+
#ifdef PCRE_CONFIG_JIT
422+
if (p->pcre1_jit_on) {
423+
pcre_free_study(p->pcre1_extra_info);
424+
pcre_jit_stack_free(p->pcre1_jit_stack);
425+
} else
426+
#endif
427+
{
428+
pcre_free(p->pcre1_extra_info);
429+
}
398430
pcre_free((void *)p->pcre1_tables);
399431
}
400432
#else /* !USE_LIBPCRE1 */

grep.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,13 @@
33
#include "color.h"
44
#ifdef USE_LIBPCRE1
55
#include <pcre.h>
6+
#ifndef PCRE_STUDY_JIT_COMPILE
7+
#define PCRE_STUDY_JIT_COMPILE 0
8+
#endif
69
#else
710
typedef int pcre;
811
typedef int pcre_extra;
12+
typedef int pcre_jit_stack;
913
#endif
1014
#include "kwset.h"
1115
#include "thread-utils.h"
@@ -48,7 +52,9 @@ struct grep_pat {
4852
regex_t regexp;
4953
pcre *pcre1_regexp;
5054
pcre_extra *pcre1_extra_info;
55+
pcre_jit_stack *pcre1_jit_stack;
5156
const unsigned char *pcre1_tables;
57+
int pcre1_jit_on;
5258
kwset_t kws;
5359
unsigned fixed:1;
5460
unsigned ignore_case:1;

0 commit comments

Comments
 (0)