summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorJim Meyering <meyering@fb.com>2020-11-25 16:49:51 -0800
committerJim Meyering <meyering@fb.com>2020-11-26 10:32:00 -0800
commit192e59903c7d313bb47de3d5c15b3dc634e98c5f (patch)
tree94f500ed106d001dcbe89d6aed7da086bde2708b /tests
parent9095818f2669ceed97c41d72c5269f60cbfa9260 (diff)
downloadgrep-192e59903c7d313bb47de3d5c15b3dc634e98c5f.tar.gz
grep: avoid performance regression with many patterns
* src/grep.c (hash_pattern): Switch from PJW to DJB2, to avoid an O(N) to O(N^2) performance regression due to hash collisions with patterns from e.g., seq 500000|tr 0-9 A-J Reported by Frank Heckenbach in https://bugs.gnu.org/44754 * NEWS (Bug fixes): Mention it. * tests/hash-collision-perf: New file. * tests/Makefile.am (TESTS): Add it.
Diffstat (limited to 'tests')
-rw-r--r--tests/Makefile.am1
-rwxr-xr-xtests/hash-collision-perf53
2 files changed, 54 insertions, 0 deletions
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 480bfb47..b1b5d61a 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -109,6 +109,7 @@ TESTS = \
grep-dev-null \
grep-dev-null-out \
grep-dir \
+ hash-collision-perf \
help-version \
high-bit-range \
in-eq-out-infloop \
diff --git a/tests/hash-collision-perf b/tests/hash-collision-perf
new file mode 100755
index 00000000..38c876de
--- /dev/null
+++ b/tests/hash-collision-perf
@@ -0,0 +1,53 @@
+#!/bin/sh
+# Test for this performance regression:
+# grep-3.5 and 3.6 would take O(N^2) time for some sets of input regexps.
+
+# Copyright 2020 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/init.sh"; path_prepend_ ../src
+
+fail=0
+
+: > empty || framework_failure_
+
+# Construct a test case that consumes enough CPU time that we don't
+# have to worry about measurement noise. This first case is searching
+# for digits, which never exhibited a problem with hash collisions.
+n_pat=40000
+while :; do
+ seq $n_pat > in || framework_failure_
+ small_ms=$(LC_ALL=C user_time_ 1 grep --file=in empty) || fail=1
+ test $small_ms -ge 200 && break
+ n_pat=$(expr $n_pat '*' 2)
+done
+
+# Now, search for those same digits mapped to A-J.
+# With the PJW-based hash function, this became O(N^2).
+seq $n_pat | tr 0-9 A-J > in || framework_failure_
+large_ms=$(LC_ALL=C user_time_ 1 grep --file=in empty) || fail=1
+
+# Deliberately recording in an unused variable so it
+# shows up in set -x output, in case this test fails.
+ratio=$(expr "$large_ms" / "$small_ms")
+warn_ ratio=$ratio
+
+# The duration of the latter run must be no more than 10 times
+# that of the former. Using recent versions prior to this fix,
+# this test would fail due to ratios > 800. Using the fixed version,
+# it's common to see a ratio less than 1.
+returns_ 1 expr $small_ms '<' $large_ms / 10 || fail=1
+
+Exit $fail