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

Re: [PATCH] grep: do not do external grep on skip-worktree entries

From
Junio C Hamano <gitster@pobox.com>
Date
Jan 11, 2010, 06:39 UTC
Message-ID
<7vvdf9402f.fsf@alter.siamese.dyndns.org>
In-Reply-To
<alpine.LFD.2.00.1001040801290.3630@localhost.localdomain>
Linus Torvalds <torvalds@linux-foundation.org> writes:
Show 7 quoted lines
> It doesn't matter. Since we do the line-by-line thing, the input is always 
> so short that DFA vs NFA vs BM vs other-clever-search doesn't matter. 
> There is no scaling - the grep buffer tends to be too small for the 
> algorithm to matter.
>
> And the reason we do things line-by-line is that we need to then output 
> things line-per-line.
Here is an experimental patch; first, some numbers (hot cache best of 5 runs).
(baseline -- builtin grep)
    $ time git grep --no-ext-grep qwerty
    drivers/char/keyboard.c:        "qwertyuiop[]\r\000as"...
    real    0m1.521s
    user    0m1.260s
    sys     0m0.256s
    (baseline -- external grep)
    $ time git grep --ext-grep qwerty
    drivers/char/keyboard.c:        "qwertyuiop[]\r\000as"...
    real    0m0.773s
    user    0m0.416s
    sys     0m0.376s
    (with experimental patch -- builtin grep)
    $ time ../git.git/git grep --no-ext-grep qwerty
    drivers/char/keyboard.c:        "qwertyuiop[]\r\000as"...
    real    0m0.692s
    user    0m0.440s
    sys     0m0.252s

The idea is to see the earliest location in the entire remainder of the buffer the pattern(s) we are looking for first appears, and skip all the lines before that point. For this optimization to be valid, we should not be looking for anything complex (e.g. "find lines that have both X and Y but not Z" is out). We cannot use the optimization when "-v" is given, either, because then we need to find the first line that does _not_ match given set of patterns.

---
 grep.c |   64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 64 insertions(+), 0 deletions(-)
diff --git a/grep.c b/grep.c
index bdadf2c..940e200 100644
--- a/grep.c
+++ b/grep.c
@@ -615,6 +615,65 @@ static void show_pre_context(struct grep_opt *opt, const char *name, char *buf,
 	}
 }
 
+static int should_lookahead(struct grep_opt *opt)
+{
+	struct grep_pat *p;
+
+	if (opt->extended)
+		return 0; /* punt for too complex stuff */
+	if (opt->invert || opt->unmatch_name_only)
+		return 0;
+	for (p = opt->pattern_list; p; p = p->next) {
+		if (p->token != GREP_PATTERN)
+			return 0; /* punt for "header only" and stuff */
+	}
+	return 1;
+}
+
+static int look_ahead(struct grep_opt *opt,
+		      unsigned long *left_p,
+		      unsigned *lno_p,
+		      char **bol_p)
+{
+	unsigned lno = *lno_p;
+	char *bol = *bol_p;
+	struct grep_pat *p;
+	char *sp, *last_bol;
+	regoff_t earliest = -1;
+
+	for (p = opt->pattern_list; p; p = p->next) {
+		int hit;
+		regmatch_t m;
+
+		if (p->fixed)
+			hit = !fixmatch(p->pattern, bol, p->ignore_case, &m);
+		else
+			hit = !regexec(&p->regexp, bol, 1, &m, 0);
+		if (!hit || m.rm_so < 0 || m.rm_eo < 0)
+			continue;
+		if (earliest < 0 || m.rm_so < earliest)
+			earliest = m.rm_so;
+	}
+
+	if (earliest < 0) {
+		*bol_p = bol + *left_p;
+		*left_p = 0;
+		return 1;
+	}
+	for (sp = bol + earliest; bol < sp && sp[-1] != '\n'; sp--)
+		; /* find the beginning of the line */
+	last_bol = sp;
+
+	for (sp = bol; sp < last_bol; sp++) {
+		if (*sp == '\n')
+			lno++;
+	}
+	*left_p -= last_bol - bol;
+	*bol_p = last_bol;
+	*lno_p = lno;
+	return 0;
+}
+
 static int grep_buffer_1(struct grep_opt *opt, const char *name,
 			 char *buf, unsigned long size, int collect_hits)
 {
@@ -624,6 +683,7 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
 	unsigned last_hit = 0;
 	int binary_match_only = 0;
 	unsigned count = 0;
+	int try_lookahead = 0;
 	enum grep_context ctx = GREP_CONTEXT_HEAD;
 	xdemitconf_t xecfg;
 
@@ -652,11 +712,15 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
 			opt->priv = &xecfg;
 		}
 	}
+	try_lookahead = should_lookahead(opt);
 
 	while (left) {
 		char *eol, ch;
 		int hit;
 
+		if (try_lookahead
+		    && look_ahead(opt, &left, &lno, &bol))
+			break;
 		eol = end_of_line(bol, &left);
 		ch = *eol;
 		*eol = 0;
Previous: Linus TorvaldsNext: Linus Torvalds
Message 25 of 60 in “grep: do not do external grep on skip-worktree entries”
  1. grep: do not do external grep on skip-worktree entriesNguyễn Thái Ngọc Duy, Dec 30, 2009
  2. Junio C HamanoDec 31, 2009
  3. Junio C HamanoDec 31, 2009
  4. Nguyen Thai Ngoc DuyJan 2, 2010
  5. Junio C HamanoJan 2, 2010
  6. Nguyen Thai Ngoc DuyJan 2, 2010
  7. Junio C HamanoJan 2, 2010
  8. Miles BaderJan 3, 2010
  9. Miles BaderJan 3, 2010
  10. Miles BaderJan 3, 2010
  11. Linus TorvaldsJan 3, 2010
  12. Junio C HamanoJan 3, 2010
  13. Jeff KingJan 4, 2010
  14. Junio C HamanoJan 4, 2010
  15. Jeff KingJan 4, 2010
  16. Junio C HamanoJan 4, 2010
  17. Junio C HamanoJan 4, 2010
  18. Jeff KingJan 4, 2010
  19. Jeff KingJan 4, 2010
  20. Jeff KingJan 4, 2010
  21. Linus TorvaldsJan 4, 2010
  22. Linus TorvaldsJan 4, 2010
  23. Miles BaderJan 4, 2010
  24. Linus TorvaldsJan 4, 2010
  25. Junio C HamanoJan 11, 2010
  26. Linus TorvaldsJan 11, 2010
  27. Linus TorvaldsJan 11, 2010
  28. Junio C HamanoJan 11, 2010
  29. Junio C HamanoJan 11, 2010
  30. Linus TorvaldsJan 11, 2010
  31. Junio C HamanoJan 12, 2010
  32. grep: lookahead optimization can be used with -L optionJunio C Hamano, Jan 12, 2010
  33. grep: -L should show empty filesJunio C Hamano, Jan 12, 2010
  34. Sverre RabbelierJan 12, 2010
  35. Junio C HamanoJan 13, 2010
  36. Sverre RabbelierJan 13, 2010
  37. Junio C HamanoJan 13, 2010
  38. 1/2 grep: rip out support for external grepJunio C Hamano, Jan 13, 2010
  39. Jay SoffianJan 13, 2010
  40. Junio C HamanoJan 13, 2010
  41. Linus TorvaldsJan 13, 2010
  42. 2/2 grep: rip out pessimization to use fixmatch()Junio C Hamano, Jan 13, 2010
  43. Jeff KingJan 12, 2010
  44. Fredrik KuivinenJan 11, 2010
  45. Linus TorvaldsJan 4, 2010
  46. Nguyen Thai Ngoc DuyJan 4, 2010
  47. Mike HommeyJan 4, 2010
  48. Jeff KingJan 4, 2010
  49. 1/2 t7002: set test prerequisite "external-grep" if supportedNguyễn Thái Ngọc Duy, Jan 4, 2010
  50. Junio C HamanoJan 7, 2010
  51. Junio C HamanoJan 7, 2010
  52. Nguyen Thai Ngoc DuyJan 7, 2010
  53. Johannes SixtJan 7, 2010
  54. Nguyen Thai Ngoc DuyJan 7, 2010
  55. 2/2 t7002: add tests for skip-worktree fixes in commit a67e281Nguyễn Thái Ngọc Duy, Jan 4, 2010
  56. Linus TorvaldsJan 11, 2010
  57. Fredrik KuivinenJan 11, 2010
  58. Linus TorvaldsJan 11, 2010
  59. Fredrik KuivinenJan 11, 2010
  60. Linus TorvaldsJan 11, 2010

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.