--- Begin Message ---
Subject: |
[PROPOSED PATCH] grep: /dev/null output speedup |
Date: |
Wed, 20 Apr 2016 10:08:48 -0700 |
This sped up 'seq 10000000000 | grep . >/dev/null' by a factor of
380,000 on my platform (Fedora 23, x86-64, AMD Phenom II X4 910e,
en_US.UTF-8 locale).
* NEWS: Document this.
* src/grep.c (grepbuf): exit_on_match no longer implies that -q
was specified, so when a match is found, exit with exit_failure if
an error was also found.
(grepdesc): Omit unnecessary S_ISREG and st_ino checks.
out_stat.st_ino is zero if stdout is not a regular file,
and this cannot possibly equal st->st_ino.
(main): Omit duplicate initialization of exit_failure. Do not
bother with isatty unless -q is not used and stdout is a character
special file and --color=auto and TERM says colorization is
possible. Most importantly, set exit_on_match if the output is
/dev/null.
* tests/grep-dev-null-out: New test.
* tests/Makefile.am (TESTS): Add it.
* tests/status: Do not require grep to actually read all the input
files when the output is /dev/null and a matching line has been
found.
---
NEWS | 2 ++
src/grep.c | 67 ++++++++++++++++++++++++++++---------------------
tests/Makefile.am | 1 +
tests/grep-dev-null-out | 11 ++++++++
tests/status | 4 +--
5 files changed, 55 insertions(+), 30 deletions(-)
create mode 100755 tests/grep-dev-null-out
diff --git a/NEWS b/NEWS
index 63767aa..2af0d6d 100644
--- a/NEWS
+++ b/NEWS
@@ -18,6 +18,8 @@ GNU grep NEWS -*- outline
-*-
** Improvements
+ grep can be much faster now when standard output is /dev/null.
+
grep now outputs details more consistently when reporting a write error.
E.g., "grep: write error: No space left on device" rather than just
"grep: write error".
diff --git a/src/grep.c b/src/grep.c
index 8baca5a..d812bae 100644
--- a/src/grep.c
+++ b/src/grep.c
@@ -1387,7 +1387,7 @@ grepbuf (char *beg, char const *lim)
if (!outleft || done_on_match)
{
if (exit_on_match)
- exit (EXIT_SUCCESS);
+ exit (errseen ? exit_failure : EXIT_SUCCESS);
break;
}
}
@@ -1751,7 +1751,6 @@ grepdesc (int desc, bool command_line)
input==output, while there is no risk of infloop, there is a race
condition that could result in "alternate" output. */
if (!out_quiet && list_files == 0 && 1 < max_count
- && S_ISREG (out_stat.st_mode) && out_stat.st_ino
&& SAME_INODE (st, out_stat))
{
if (! suppress_errors)
@@ -2280,7 +2279,6 @@ main (int argc, char **argv)
textdomain (PACKAGE);
#endif
- exit_failure = EXIT_TROUBLE;
atexit (clean_up_stdout);
last_recursive = 0;
@@ -2579,25 +2577,36 @@ main (int argc, char **argv)
}
- if (color_option == 2)
- color_option = isatty (STDOUT_FILENO) && should_colorize ();
- init_colorize ();
+ if (show_version)
+ {
+ version_etc (stdout, program_name, PACKAGE_NAME, VERSION, AUTHORS,
+ (char *) NULL);
+ return EXIT_SUCCESS;
+ }
- /* POSIX says that -q overrides -l, which in turn overrides the
- other output options. */
- if (exit_on_match)
- list_files = 0;
- if (exit_on_match | list_files)
+ if (show_help)
+ usage (EXIT_SUCCESS);
+
+ bool possibly_tty = false;
+ struct stat tmp_stat;
+ if (! exit_on_match && fstat (STDOUT_FILENO, &tmp_stat) == 0)
{
- count_matches = false;
- done_on_match = true;
+ if (S_ISREG (tmp_stat.st_mode))
+ out_stat = tmp_stat;
+ else if (S_ISCHR (tmp_stat.st_mode))
+ {
+ struct stat null_stat;
+ if (stat ("/dev/null", &null_stat) == 0
+ && SAME_INODE (tmp_stat, null_stat))
+ exit_on_match = true;
+ else
+ possibly_tty = true;
+ }
}
- out_quiet = count_matches | done_on_match;
- if (out_after < 0)
- out_after = default_context;
- if (out_before < 0)
- out_before = default_context;
+ if (color_option == 2)
+ color_option = possibly_tty && should_colorize () && isatty
(STDOUT_FILENO);
+ init_colorize ();
if (color_option)
{
@@ -2610,19 +2619,21 @@ main (int argc, char **argv)
parse_grep_colors ();
}
- if (show_version)
+ /* POSIX says -c, -l and -q are mutually exclusive. In this
+ implementation, -q overrides -l and -L, which in turn override -c. */
+ if (exit_on_match)
+ list_files = 0;
+ if (exit_on_match | list_files)
{
- version_etc (stdout, program_name, PACKAGE_NAME, VERSION, AUTHORS,
- (char *) NULL);
- return EXIT_SUCCESS;
+ count_matches = false;
+ done_on_match = true;
}
+ out_quiet = count_matches | done_on_match;
- if (show_help)
- usage (EXIT_SUCCESS);
-
- struct stat tmp_stat;
- if (fstat (STDOUT_FILENO, &tmp_stat) == 0 && S_ISREG (tmp_stat.st_mode))
- out_stat = tmp_stat;
+ if (out_after < 0)
+ out_after = default_context;
+ if (out_before < 0)
+ out_before = default_context;
if (keys)
{
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 45908ce..7effa57 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -82,6 +82,7 @@ TESTS = \
fmbtest \
foad1 \
grep-dev-null \
+ grep-dev-null-out \
grep-dir \
help-version \
high-bit-range \
diff --git a/tests/grep-dev-null-out b/tests/grep-dev-null-out
new file mode 100755
index 0000000..f30700d
--- /dev/null
+++ b/tests/grep-dev-null-out
@@ -0,0 +1,11 @@
+#!/bin/sh
+# Outputting to /dev/null.
+
+. "${srcdir=.}/init.sh"; path_prepend_ ../src
+
+require_timeout_
+
+${AWK-awk} 'BEGIN {while (1) print "x"}' </dev/null |
+ timeout 1 grep x >/dev/null || fail=1
+
+Exit $fail
diff --git a/tests/status b/tests/status
index 9de98df..2a2d6e0 100755
--- a/tests/status
+++ b/tests/status
@@ -47,9 +47,9 @@ else
fail=1
fi
- # should return 2 file not found
+ # should return 0 (found a match) or 2 (file not found)
echo "abcd" | grep -E -s 'abc' - MMMMMMMM.MMM > /dev/null 2>&1
- if test $? -ne 2 ; then
+ if test $? -ne 0 && test $? -ne 2 ; then
echo "Status: Wrong status code, test \#5 failed"
fail=1
fi
--
2.5.5
--- End Message ---