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 63e7e9d8b6 ("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 <avarab@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
			
			
				maint
			
			
		
							parent
							
								
									7531a2dd87
								
							
						
					
					
						commit
						fbaceaac47
					
				
							
								
								
									
										40
									
								
								grep.c
								
								
								
								
							
							
						
						
									
										40
									
								
								grep.c
								
								
								
								
							|  | @ -365,9 +365,22 @@ static void compile_pcre1_regexp(struct grep_pat *p, const struct grep_opt *opt) | ||||||
| 	if (!p->pcre1_regexp) | 	if (!p->pcre1_regexp) | ||||||
| 		compile_regexp_failed(p, error); | 		compile_regexp_failed(p, error); | ||||||
|  |  | ||||||
| 	p->pcre1_extra_info = pcre_study(p->pcre1_regexp, 0, &error); | 	p->pcre1_extra_info = pcre_study(p->pcre1_regexp, PCRE_STUDY_JIT_COMPILE, &error); | ||||||
| 	if (!p->pcre1_extra_info && error) | 	if (!p->pcre1_extra_info && error) | ||||||
| 		die("%s", error); | 		die("%s", error); | ||||||
|  |  | ||||||
|  | #ifdef PCRE_CONFIG_JIT | ||||||
|  | 	pcre_config(PCRE_CONFIG_JIT, &p->pcre1_jit_on); | ||||||
|  | 	if (p->pcre1_jit_on == 1) { | ||||||
|  | 		p->pcre1_jit_stack = pcre_jit_stack_alloc(1, 1024 * 1024); | ||||||
|  | 		if (!p->pcre1_jit_stack) | ||||||
|  | 			die("Couldn't allocate PCRE JIT stack"); | ||||||
|  | 		pcre_assign_jit_stack(p->pcre1_extra_info, NULL, p->pcre1_jit_stack); | ||||||
|  | 	} else if (p->pcre1_jit_on != 0) { | ||||||
|  | 		die("BUG: The pcre1_jit_on variable should be 0 or 1, not %d", | ||||||
|  | 		    p->pcre1_jit_on); | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
| } | } | ||||||
|  |  | ||||||
| static int pcre1match(struct grep_pat *p, const char *line, const char *eol, | 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, | ||||||
| 	if (eflags & REG_NOTBOL) | 	if (eflags & REG_NOTBOL) | ||||||
| 		flags |= PCRE_NOTBOL; | 		flags |= PCRE_NOTBOL; | ||||||
|  |  | ||||||
| 	ret = pcre_exec(p->pcre1_regexp, p->pcre1_extra_info, line, eol - line, | #ifdef PCRE_CONFIG_JIT | ||||||
| 			0, flags, ovector, ARRAY_SIZE(ovector)); | 	if (p->pcre1_jit_on) { | ||||||
|  | 		ret = pcre_jit_exec(p->pcre1_regexp, p->pcre1_extra_info, line, | ||||||
|  | 				    eol - line, 0, flags, ovector, | ||||||
|  | 				    ARRAY_SIZE(ovector), p->pcre1_jit_stack); | ||||||
|  | 	} else | ||||||
|  | #endif | ||||||
|  | 	{ | ||||||
|  | 		ret = pcre_exec(p->pcre1_regexp, p->pcre1_extra_info, line, | ||||||
|  | 				eol - line, 0, flags, ovector, | ||||||
|  | 				ARRAY_SIZE(ovector)); | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	if (ret < 0 && ret != PCRE_ERROR_NOMATCH) | 	if (ret < 0 && ret != PCRE_ERROR_NOMATCH) | ||||||
| 		die("pcre_exec failed with error code %d", ret); | 		die("pcre_exec failed with error code %d", ret); | ||||||
| 	if (ret > 0) { | 	if (ret > 0) { | ||||||
|  | @ -394,7 +418,15 @@ static int pcre1match(struct grep_pat *p, const char *line, const char *eol, | ||||||
| static void free_pcre1_regexp(struct grep_pat *p) | static void free_pcre1_regexp(struct grep_pat *p) | ||||||
| { | { | ||||||
| 	pcre_free(p->pcre1_regexp); | 	pcre_free(p->pcre1_regexp); | ||||||
| 	pcre_free(p->pcre1_extra_info); | #ifdef PCRE_CONFIG_JIT | ||||||
|  | 	if (p->pcre1_jit_on) { | ||||||
|  | 		pcre_free_study(p->pcre1_extra_info); | ||||||
|  | 		pcre_jit_stack_free(p->pcre1_jit_stack); | ||||||
|  | 	} else | ||||||
|  | #endif | ||||||
|  | 	{ | ||||||
|  | 		pcre_free(p->pcre1_extra_info); | ||||||
|  | 	} | ||||||
| 	pcre_free((void *)p->pcre1_tables); | 	pcre_free((void *)p->pcre1_tables); | ||||||
| } | } | ||||||
| #else /* !USE_LIBPCRE1 */ | #else /* !USE_LIBPCRE1 */ | ||||||
|  |  | ||||||
							
								
								
									
										6
									
								
								grep.h
								
								
								
								
							
							
						
						
									
										6
									
								
								grep.h
								
								
								
								
							|  | @ -3,9 +3,13 @@ | ||||||
| #include "color.h" | #include "color.h" | ||||||
| #ifdef USE_LIBPCRE1 | #ifdef USE_LIBPCRE1 | ||||||
| #include <pcre.h> | #include <pcre.h> | ||||||
|  | #ifndef PCRE_STUDY_JIT_COMPILE | ||||||
|  | #define PCRE_STUDY_JIT_COMPILE 0 | ||||||
|  | #endif | ||||||
| #else | #else | ||||||
| typedef int pcre; | typedef int pcre; | ||||||
| typedef int pcre_extra; | typedef int pcre_extra; | ||||||
|  | typedef int pcre_jit_stack; | ||||||
| #endif | #endif | ||||||
| #include "kwset.h" | #include "kwset.h" | ||||||
| #include "thread-utils.h" | #include "thread-utils.h" | ||||||
|  | @ -48,7 +52,9 @@ struct grep_pat { | ||||||
| 	regex_t regexp; | 	regex_t regexp; | ||||||
| 	pcre *pcre1_regexp; | 	pcre *pcre1_regexp; | ||||||
| 	pcre_extra *pcre1_extra_info; | 	pcre_extra *pcre1_extra_info; | ||||||
|  | 	pcre_jit_stack *pcre1_jit_stack; | ||||||
| 	const unsigned char *pcre1_tables; | 	const unsigned char *pcre1_tables; | ||||||
|  | 	int pcre1_jit_on; | ||||||
| 	kwset_t kws; | 	kwset_t kws; | ||||||
| 	unsigned fixed:1; | 	unsigned fixed:1; | ||||||
| 	unsigned ignore_case:1; | 	unsigned ignore_case:1; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Ævar Arnfjörð Bjarmason
						Ævar Arnfjörð Bjarmason