With -B N (and -C N), prtext's leading-context loop always iterated
N times per matching line, even after reaching the start of unprinted
input, where remaining iterations are no-ops.  With many matching
lines and large N, this cost O(N) time per match -- quadratic overall
behavior -- unless the compiler happened to rescue it (GCC removes
the no-op tail only with -fsplit-loops, enabled at -O3 but not at
the default -O2).  For example, with a stock '-g -O2' build on
20,000 matching lines, 'grep -B 1000000 x' took 5.1 s versus 0.01 s
for 'grep -B 1000'; after this change both take 0.01 s.  Exit the
loop as soon as the boundary is reached, as the analogous loop in
grep() already does; output is unchanged.
* src/grep.c (prtext): Move the 'p > bp' test from the loop body
into the loop condition, so the loop exits once P reaches BP
instead of continuing to iterate up to OUT_BEFORE times.
---
 src/grep.c | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/src/grep.c b/src/grep.c
index 841e225..8e4ecff 100644
--- a/src/grep.c
+++ b/src/grep.c
@@ -1383,11 +1383,10 @@ prtext (char *beg, char *lim)
       /* Deal with leading context.  */
       char const *bp = lastout ? lastout : bufbeg;
       intmax_t i;
-      for (i = 0; i < out_before; ++i)
-        if (p > bp)
-          do
-            --p;
-          while (p[-1] != eol);
+      for (i = 0; i < out_before && p > bp; ++i)
+        do
+          --p;
+        while (p[-1] != eol);
 
       /* Print the group separator unless the output is adjacent to
          the previous output in the file.  */
-- 
2.43.0




Reply via email to