diff options
author | Paul Eggert <eggert@cs.ucla.edu> | 2016-05-01 22:56:39 -0700 |
---|---|---|
committer | Paul Eggert <eggert@cs.ucla.edu> | 2016-05-01 22:56:39 -0700 |
commit | af6af288eac28951b5eee1eaaf373e22b2193b7b (patch) | |
tree | 41dcd89cb183bbf314984f41b676755cc59047a2 | |
parent | d1060aa9f7d1daca37656a35751241ddce67ae75 (diff) | |
download | grep-af6af288eac28951b5eee1eaaf373e22b2193b7b.tar.gz |
grep: /dev/null output speedup
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.
-rw-r--r-- | NEWS | 2 | ||||
-rw-r--r-- | src/grep.c | 67 | ||||
-rw-r--r-- | tests/Makefile.am | 1 | ||||
-rwxr-xr-x | tests/grep-dev-null-out | 11 | ||||
-rwxr-xr-x | tests/status | 4 |
5 files changed, 55 insertions, 30 deletions
@@ -21,6 +21,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". @@ -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 45908ce2..7effa571 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 00000000..f30700dd --- /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 9de98dff..2a2d6e04 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 |