Binary-file detection called buf_has_nulls on the whole buffer at
every refill, rescanning the saved leading-context bytes that were
already checked when first read. With -B N or -C N large enough that
the saved region dwarfs the 96 KiB read size, this rescan dominated:
a matchless search of a 3.4 GB log file with -C 1000000 spent over
600 s in strlen alone, versus 5 s once only new bytes are checked.
Detection behavior is unchanged: any null byte among the saved bytes
was already detected in the iteration that read those bytes.
* src/grep.c (grep): Pass only the newly read data, which starts SAVE
bytes into the buffer, to buf_has_nulls.
* NEWS: Mention the -B/-C performance improvements.
---
NEWS | 9 +++++++++
src/grep.c | 5 ++++-
2 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/NEWS b/NEWS
index 5049a85..29aad92 100644
--- a/NEWS
+++ b/NEWS
@@ -18,6 +18,15 @@ GNU grep NEWS -*- outline
-*-
On Windows platforms and on AIX in 32-bit mode, grep now fully
supports Unicode characters outside the Basic Multilingual Plane.
+** Improvements
+
+ With -B N or -C N, grep no longer spends time proportional to N per
+ buffer refill and per matching line, which made large N quadratically
+ slow. For example, searching a 3.4 GB log file for a pattern that
+ never matches took 1.3 s with -C 1 but 34 s with -C 10000; the
+ latter now takes 2.5 s.
+ [bug present since "the beginning"]
+
* Noteworthy changes in release 3.12 (2025-04-10) [stable]
diff --git a/src/grep.c b/src/grep.c
index a5afbe5..8c1d103 100644
--- a/src/grep.c
+++ b/src/grep.c
@@ -1558,8 +1558,11 @@ grep (int fd, struct stat const *st, bool *ineof)
for (bool firsttime = true; ; firsttime = false)
{
+ /* Check only the newly read bytes for null bytes; the first SAVE
+ bytes are saved leading context and residue, already checked
+ when first read. */
if (nlines_first_null < 0 && eol && binary_files != TEXT_BINARY_FILES
- && (buf_has_nulls (bufbeg, buflim - bufbeg)
+ && (buf_has_nulls (bufbeg + save, buflim - bufbeg - save)
|| (firsttime && file_must_have_nulls (buflim - bufbeg, fd,
st))))
{
if (binary_files == WITHOUT_MATCH_BINARY_FILES)
--
2.43.0