git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v3 10/10] grep: use PCRE v2 for optimized fixed-string search

From
Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Date
Jul 1, 2019, 21:21 UTC
Message-ID
<20190701212100.27850-11-avarab@gmail.com>
In-Reply-To
<20190627233912.7117-1-avarab@gmail.com>

Bring back optimized fixed-string search for "grep", this time with PCRE v2 as an optional backend. As noted in [1] with kwset we were slower than PCRE v1 and v2 JIT with the kwset backend, so that optimization was counterproductive.

This brings back the optimization for "--fixed-strings", without changing the semantics of having a NUL-byte in patterns. As seen in previous commits in this series we could support it now, but I'd rather just leave that edge-case aside so we don't have one behavior or the other depending what "--fixed-strings" backend we're using. It makes the behavior harder to understand and document, and makes tests for the different backends more painful.

This does change the behavior under non-C locales when "log"'s "--encoding" option is used and the heystack/needle in the content/command-line doesn't have a matching encoding. See the recent change in "t4210: skip more command-line encoding tests on MinGW" in this series. I think that's OK. We did nothing sensible before then (just compared raw bytes that had no hope of matching). At least now the user will get some idea why their grep/log never matches in that edge case.

I could also support the PCRE v1 backend here, but that would make the code more complex. I'd rather aim for simplicity here and in future changes to the diffcore. We're not going to have someone who absolutely must have faster search, but for whom building PCRE v2 isn't acceptable.

The difference between this series of commits and the current "master" is, using the same t/perf commands shown in the last commit:

plain grep:
    Test                             origin/master     HEAD
    -------------------------------------------------------------------------
    7821.1: fixed grep int           0.55(1.67+0.56)   0.41(0.98+0.60) -25.5%
    7821.2: basic grep int           0.58(1.65+0.52)   0.41(0.96+0.57) -29.3%
    7821.3: extended grep int        0.57(1.66+0.49)   0.42(0.93+0.60) -26.3%
    7821.4: perl grep int            0.54(1.67+0.50)   0.43(0.88+0.65) -20.4%
    7821.6: fixed grep uncommon      0.21(0.52+0.42)   0.16(0.24+0.51) -23.8%
    7821.7: basic grep uncommon      0.20(0.49+0.45)   0.17(0.28+0.47) -15.0%
    7821.8: extended grep uncommon   0.20(0.54+0.39)   0.16(0.25+0.50) -20.0%
    7821.9: perl grep uncommon       0.20(0.58+0.36)   0.16(0.23+0.50) -20.0%
    7821.11: fixed grep æ            0.35(1.24+0.43)   0.16(0.23+0.50) -54.3%
    7821.12: basic grep æ            0.36(1.29+0.38)   0.16(0.20+0.54) -55.6%
    7821.13: extended grep æ         0.35(1.23+0.44)   0.16(0.24+0.50) -54.3%
    7821.14: perl grep æ             0.35(1.33+0.34)   0.16(0.28+0.46) -54.3%
grep with -i:
    Test                                origin/master     HEAD
    ----------------------------------------------------------------------------
    7821.1: fixed grep -i int           0.62(1.81+0.70)   0.47(1.11+0.64) -24.2%
    7821.2: basic grep -i int           0.67(1.90+0.53)   0.46(1.07+0.62) -31.3%
    7821.3: extended grep -i int        0.62(1.92+0.53)   0.53(1.12+0.58) -14.5%
    7821.4: perl grep -i int            0.66(1.85+0.58)   0.45(1.10+0.59) -31.8%
    7821.6: fixed grep -i uncommon      0.21(0.54+0.43)   0.17(0.20+0.55) -19.0%
    7821.7: basic grep -i uncommon      0.20(0.52+0.45)   0.17(0.29+0.48) -15.0%
    7821.8: extended grep -i uncommon   0.21(0.52+0.44)   0.17(0.26+0.50) -19.0%
    7821.9: perl grep -i uncommon       0.21(0.53+0.44)   0.17(0.20+0.56) -19.0%
    7821.11: fixed grep -i æ            0.26(0.79+0.44)   0.16(0.29+0.46) -38.5%
    7821.12: basic grep -i æ            0.26(0.79+0.42)   0.16(0.20+0.54) -38.5%
    7821.13: extended grep -i æ         0.26(0.84+0.39)   0.16(0.24+0.50) -38.5%
    7821.14: perl grep -i æ             0.16(0.24+0.49)   0.17(0.25+0.51) +6.3%
plain log:
    Test                                     origin/master     HEAD
    --------------------------------------------------------------------------------
    4221.1: fixed log --grep='int'           7.24(6.95+0.28)   7.20(6.95+0.18) -0.6%
    4221.2: basic log --grep='int'           7.31(6.97+0.22)   7.20(6.93+0.21) -1.5%
    4221.3: extended log --grep='int'        7.37(7.04+0.24)   7.22(6.91+0.25) -2.0%
    4221.4: perl log --grep='int'            7.31(7.04+0.21)   7.19(6.89+0.21) -1.6%
    4221.6: fixed log --grep='uncommon'      6.93(6.59+0.32)   7.04(6.66+0.37) +1.6%
    4221.7: basic log --grep='uncommon'      6.92(6.58+0.29)   7.08(6.75+0.29) +2.3%
    4221.8: extended log --grep='uncommon'   6.92(6.55+0.31)   7.00(6.68+0.31) +1.2%
    4221.9: perl log --grep='uncommon'       7.03(6.59+0.33)   7.12(6.73+0.34) +1.3%
    4221.11: fixed log --grep='æ'            7.41(7.08+0.28)   7.05(6.76+0.29) -4.9%
    4221.12: basic log --grep='æ'            7.39(6.99+0.33)   7.00(6.68+0.25) -5.3%
    4221.13: extended log --grep='æ'         7.34(7.00+0.25)   7.15(6.81+0.31) -2.6%
    4221.14: perl log --grep='æ'             7.43(7.13+0.26)   7.01(6.60+0.36) -5.7%
log with -i:
    Test                                        origin/master     HEAD
    ------------------------------------------------------------------------------------
    4221.1: fixed log -i --grep='int'           7.31(7.07+0.24)   7.23(7.00+0.22) -1.1%
    4221.2: basic log -i --grep='int'           7.40(7.08+0.28)   7.19(6.92+0.20) -2.8%
    4221.3: extended log -i --grep='int'        7.43(7.13+0.25)   7.27(6.99+0.21) -2.2%
    4221.4: perl log -i --grep='int'            7.34(7.10+0.24)   7.10(6.90+0.19) -3.3%
    4221.6: fixed log -i --grep='uncommon'      7.07(6.71+0.32)   7.11(6.77+0.28) +0.6%
    4221.7: basic log -i --grep='uncommon'      6.99(6.64+0.28)   7.12(6.69+0.38) +1.9%
    4221.8: extended log -i --grep='uncommon'   7.11(6.74+0.32)   7.10(6.77+0.27) -0.1%
    4221.9: perl log -i --grep='uncommon'       6.98(6.60+0.29)   7.05(6.64+0.34) +1.0%
    4221.11: fixed log -i --grep='æ'            7.85(7.45+0.34)   7.03(6.68+0.32) -10.4%
    4221.12: basic log -i --grep='æ'            7.87(7.49+0.29)   7.06(6.69+0.31) -10.3%
    4221.13: extended log -i --grep='æ'         7.87(7.54+0.31)   7.09(6.69+0.31) -9.9%
    4221.14: perl log -i --grep='æ'             7.06(6.77+0.28)   6.91(6.57+0.31) -2.1%

So as with e05b027627 ("grep: use PCRE v2 for optimized fixed-string search", 2019-06-26) there's a huge improvement in performance for "grep", but in "log" most of our time is spent elsewhere, so we don't notice it that much.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 grep.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 49 insertions(+), 2 deletions(-)
diff --git a/grep.c b/grep.c
index 4468519d5c..fc0ed73ef3 100644
--- a/grep.c
+++ b/grep.c
@@ -356,6 +356,18 @@ static NORETURN void compile_regexp_failed(const struct grep_pat *p,
 	die("%s'%s': %s", where, p->pattern, error);
 }
 
+static int is_fixed(const char *s, size_t len)
+{
+	size_t i;
+
+	for (i = 0; i < len; i++) {
+		if (is_regex_special(s[i]))
+			return 0;
+	}
+
+	return 1;
+}
+
 #ifdef USE_LIBPCRE1
 static void compile_pcre1_regexp(struct grep_pat *p, const struct grep_opt *opt)
 {
@@ -602,7 +614,6 @@ static int pcre2match(struct grep_pat *p, const char *line, const char *eol,
 static void free_pcre2_pattern(struct grep_pat *p)
 {
 }
-#endif /* !USE_LIBPCRE2 */
 
 static void compile_fixed_regexp(struct grep_pat *p, struct grep_opt *opt)
 {
@@ -623,11 +634,13 @@ static void compile_fixed_regexp(struct grep_pat *p, struct grep_opt *opt)
 		compile_regexp_failed(p, errbuf);
 	}
 }
+#endif /* !USE_LIBPCRE2 */
 
 static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
 {
 	int err;
 	int regflags = REG_NEWLINE;
+	int pat_is_fixed;
 
 	p->word_regexp = opt->word_regexp;
 	p->ignore_case = opt->ignore_case;
@@ -636,8 +649,42 @@ static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
 	if (memchr(p->pattern, 0, p->patternlen) && !opt->pcre2)
 		die(_("given pattern contains NULL byte (via -f <file>). This is only supported with -P under PCRE v2"));
 
-	if (opt->fixed) {
+	pat_is_fixed = is_fixed(p->pattern, p->patternlen);
+	if (opt->fixed || pat_is_fixed) {
+#ifdef USE_LIBPCRE2
+		opt->pcre2 = 1;
+		if (pat_is_fixed) {
+			compile_pcre2_pattern(p, opt);
+		} else {
+			/*
+			 * E.g. t7811-grep-open.sh relies on the
+			 * pattern being restored.
+			 */
+			char *old_pattern = p->pattern;
+			size_t old_patternlen = p->patternlen;
+			struct strbuf sb = STRBUF_INIT;
+
+			/*
+			 * There is the PCRE2_LITERAL flag, but it's
+			 * only in PCRE v2 10.30 and later. Needing to
+			 * ifdef our way around that and dealing with
+			 * it + PCRE2_MULTILINE being an error is more
+			 * complex than just quoting this ourselves.
+			*/
+			strbuf_add(&sb, "\\Q", 2);
+			strbuf_add(&sb, p->pattern, p->patternlen);
+			strbuf_add(&sb, "\\E", 2);
+
+			p->pattern = sb.buf;
+			p->patternlen = sb.len;
+			compile_pcre2_pattern(p, opt);
+			p->pattern = old_pattern;
+			p->patternlen = old_patternlen;
+			strbuf_release(&sb);
+		}
+#else /* !USE_LIBPCRE2 */
 		compile_fixed_regexp(p, opt);
+#endif /* !USE_LIBPCRE2 */
 		return;
 	}
 
-- 
2.22.0.455.g172b71a6c5
Previous: Ævar Arnfjörð BjarmasonNext: Ævar Arnfjörð Bjarmason
Message 65 of 90 in “Support building with GCC v8.x/v9.x”
  1. 0/4 Support building with GCC v8.x/v9.xJohannes Schindelin via GitGitGadget, Jun 13, 2019
  2. 4/4 config: avoid calling `labs()` on too-large data typeJohannes Schindelin via GitGitGadget, Jun 13, 2019
  3. Junio C HamanoJun 13, 2019
  4. René ScharfeJun 16, 2019
  5. René ScharfeJun 16, 2019
  6. René ScharfeJun 16, 2019
  7. Junio C HamanoJun 16, 2019
  8. René ScharfeJun 20, 2019
  9. Junio C HamanoJun 20, 2019
  10. Johannes SchindelinJun 21, 2019
  11. René ScharfeJun 22, 2019
  12. 1/3 config: use unsigned_mult_overflows to check for overflowsRené Scharfe, Jun 22, 2019
  13. 2/3 config: don't multiply in parse_unit_factor()René Scharfe, Jun 22, 2019
  14. 3/3 config: simplify parsing of unit factorsRené Scharfe, Jun 22, 2019
  15. 3/4 winansi: simplify loading the GetCurrentConsoleFontEx() functionJohannes Schindelin via GitGitGadget, Jun 13, 2019
  16. 1/4 poll (mingw): allow compiling with GCC 8 and DEVELOPER=1Johannes Schindelin via GitGitGadget, Jun 13, 2019
  17. 2/4 kwset: allow building with GCC 8Johannes Schindelin via GitGitGadget, Jun 13, 2019
  18. Junio C HamanoJun 13, 2019
  19. SZEDER GáborJun 14, 2019
  20. 0/4 compat/obstack: update from upstreamSZEDER Gábor, Jun 14, 2019
  21. 2/4 SQUASH??? compat/obstack: fix portability issuesSZEDER Gábor, Jun 14, 2019
  22. 4/4 compat/obstack: fix some sparse warningsSZEDER Gábor, Jun 14, 2019
  23. 3/4 SQUASH??? compat/obstack: fix build errors with ClangSZEDER Gábor, Jun 14, 2019
  24. 1/4 compat/obstack: update from upstreamSZEDER Gábor, Jun 14, 2019
  25. Jeff KingJun 14, 2019
  26. Junio C HamanoJun 14, 2019
  27. Ramsay JonesJun 14, 2019
  28. Ramsay JonesJun 14, 2019
  29. SZEDER GáborJun 17, 2019
  30. Junio C HamanoJun 14, 2019
  31. SZEDER GáborJun 17, 2019
  32. Ævar Arnfjörð BjarmasonJun 14, 2019
  33. Can we just get rid of kwset & obstack in favor of optimistically using PCRE v2 JIT?Ævar Arnfjörð Bjarmason, Jun 14, 2019
  34. Ævar Arnfjörð BjarmasonJun 14, 2019
  35. Jeff KingJun 20, 2019
  36. Carlo ArenasJun 15, 2019
  37. brian m. carlsonJun 15, 2019
  38. Ævar Arnfjörð BjarmasonJun 15, 2019
  39. 0/7 grep: move from kwset to optional PCRE v2Ævar Arnfjörð Bjarmason, Jun 26, 2019
  40. Johannes SchindelinJun 26, 2019
  41. Johannes SchindelinJun 27, 2019
  42. Ævar Arnfjörð BjarmasonJun 27, 2019
  43. Johannes SchindelinJun 27, 2019
  44. 1/9 log tests: test regex backends in "--encode=<enc>" testsÆvar Arnfjörð Bjarmason, Jun 27, 2019
  45. 0/9 grep: move from kwset to optional PCRE v2Ævar Arnfjörð Bjarmason, Jun 27, 2019
  46. Ævar Arnfjörð BjarmasonJun 28, 2019
  47. Junio C HamanoJun 28, 2019
  48. 00/10 grep: move from kwset to optional PCRE v2Ævar Arnfjörð Bjarmason, Jul 1, 2019
  49. Junio C HamanoJul 1, 2019
  50. Ævar Arnfjörð BjarmasonJul 2, 2019
  51. Johannes SchindelinJul 2, 2019
  52. Junio C HamanoJul 2, 2019
  53. Johannes SchindelinJul 3, 2019
  54. Johannes SchindelinJul 3, 2019
  55. Johannes SchindelinJul 3, 2019
  56. 01/10 log tests: test regex backends in "--encode=<enc>" testsÆvar Arnfjörð Bjarmason, Jul 1, 2019
  57. 02/10 grep: don't use PCRE2?_UTF8 with "log --encoding=<non-utf8>"Ævar Arnfjörð Bjarmason, Jul 1, 2019
  58. 03/10 t4210: skip more command-line encoding tests on MinGWÆvar Arnfjörð Bjarmason, Jul 1, 2019
  59. 04/10 grep: inline the return value of a function call used only onceÆvar Arnfjörð Bjarmason, Jul 1, 2019
  60. 05/10 grep tests: move "grep binary" alongside the restÆvar Arnfjörð Bjarmason, Jul 1, 2019
  61. 08/10 grep: drop support for \0 in --fixed-strings <pattern>Ævar Arnfjörð Bjarmason, Jul 1, 2019
  62. 07/10 grep: make the behavior for NUL-byte in patterns saneÆvar Arnfjörð Bjarmason, Jul 1, 2019
  63. 06/10 grep tests: move binary pattern tests into their own fileÆvar Arnfjörð Bjarmason, Jul 1, 2019
  64. 09/10 grep: remove the kwset optimizationÆvar Arnfjörð Bjarmason, Jul 1, 2019
  65. 10/10 grep: use PCRE v2 for optimized fixed-string searchÆvar Arnfjörð Bjarmason, Jul 1, 2019
  66. 2/9 grep: don't use PCRE2?_UTF8 with "log --encoding=<non-utf8>"Ævar Arnfjörð Bjarmason, Jun 27, 2019
  67. 3/9 grep: inline the return value of a function call used only onceÆvar Arnfjörð Bjarmason, Jun 27, 2019
  68. 4/9 grep tests: move "grep binary" alongside the restÆvar Arnfjörð Bjarmason, Jun 27, 2019
  69. 5/9 grep tests: move binary pattern tests into their own fileÆvar Arnfjörð Bjarmason, Jun 27, 2019
  70. 6/9 grep: make the behavior for NUL-byte in patterns saneÆvar Arnfjörð Bjarmason, Jun 27, 2019
  71. 7/9 grep: drop support for \0 in --fixed-strings <pattern>Ævar Arnfjörð Bjarmason, Jun 27, 2019
  72. 8/9 grep: remove the kwset optimizationÆvar Arnfjörð Bjarmason, Jun 27, 2019
  73. 9/9 grep: use PCRE v2 for optimized fixed-string searchÆvar Arnfjörð Bjarmason, Jun 27, 2019
  74. 1/7 grep: inline the return value of a function call used only onceÆvar Arnfjörð Bjarmason, Jun 26, 2019
  75. 2/7 grep tests: move "grep binary" alongside the restÆvar Arnfjörð Bjarmason, Jun 26, 2019
  76. Johannes SchindelinJun 26, 2019
  77. Junio C HamanoJun 26, 2019
  78. 4/7 grep: make the behavior for \0 in patterns saneÆvar Arnfjörð Bjarmason, Jun 26, 2019
  79. brian m. carlsonJun 27, 2019
  80. 3/7 grep tests: move binary pattern tests into their own fileÆvar Arnfjörð Bjarmason, Jun 26, 2019
  81. 5/7 grep: drop support for \0 in --fixed-strings <pattern>Ævar Arnfjörð Bjarmason, Jun 26, 2019
  82. Junio C HamanoJun 26, 2019
  83. 6/7 grep: remove the kwset optimizationÆvar Arnfjörð Bjarmason, Jun 26, 2019
  84. 7/7 grep: use PCRE v2 for optimized fixed-string searchÆvar Arnfjörð Bjarmason, Jun 26, 2019
  85. Johannes SchindelinJun 26, 2019
  86. Junio C HamanoJun 26, 2019
  87. Johannes SchindelinJun 27, 2019
  88. Johannes SchindelinJun 27, 2019
  89. Junio C HamanoJun 27, 2019
  90. Johannes SchindelinJun 28, 2019

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.