15

我很惊讶地发现,当您添加该--ignore-case选项时grep,它会使搜索速度减慢 50 倍。我已经在两台不同的机器上进行了测试,结果相同。我很想知道巨大的性能差异的解释。

我还希望看到 grep 的替代命令以进行不区分大小写的搜索。我不需要正则表达式,只需要固定字符串搜索。首先,测试文件将是一个 50 MB 的纯文本文件,其中包含一些虚拟数据,您可以使用以下代码生成它:

创建 test.txt

yes all work and no play makes Jack a dull boy | head -c 50M > test.txt
echo "Jack is no fun" >> test.txt
echo "Jack is no Fun" >> test.txt

示范

下面是缓慢的演示。通过添加该--ignore-case选项,命令会慢 57 倍。

$ time grep fun test.txt
all work and no plJack is no fun
real    0m0.061s

$ time grep --ignore-case fun test.txt
all work and no plJack is no fun
Jack is no Fun
real    0m3.498s

可能的解释

谷歌搜索我发现一个关于 grep 在 UTF-8 语言环境中速度慢的讨论。所以我进行了以下测试,它确实加快了速度。我机器上的默认语言环境是en_US.UTF-8,因此将其设置为POSIX似乎已经启动了性能,但现在我当然无法正确搜索 Unicode 文本,这是不受欢迎的。它仍然慢了 2.5 倍。

$ time LANG=POSIX grep --ignore-case fun test.txt
all work and no plJack is no fun
Jack is no Fun
real    0m0.142s

备择方案

我们可以改用 Perl,它更快,但仍然比区分大小写的 grep 快 5.5 倍。上面的 POSIX grep 大约快两倍。

$ time perl -ne '/fun/i && print' test.txt
all work and no plJack is no fun
Jack is no Fun
real    0m0.388s

因此,如果有人有的话,我很想找到一个快速正确的替代方案和解释。

更新-CentOS

上面测试的两台机器都运行 Ubuntu,一台 11.04(Natty Narwhal),另一台 12.04(Precise Pangolin)。在 CentOS 5.3 机器上运行相同的测试会产生以下有趣的结果。两种情况的性能结果几乎相同。现在 CentOS 5.3 于 2009 年 1 月发布,运行 grep 2.5.1,而 Ubuntu 12.04 运行 grep 2.10。因此,新版本可能会发生变化,两个发行版可能会有所不同。

$ time grep fun test.txt
Jack is no fun
real    0m0.026s

$ time grep --ignore-case fun test.txt
Jack is no fun
Jack is no Fun
real    0m0.027s
4

4 回答 4

8

我认为这个错误报告有助于理解为什么它很慢:

错误报告 grep,忽略大小写速度慢

于 2012-12-11T12:01:16.103 回答
8

这种缓慢是由于 grep(在 UTF-8 语言环境上)不断访问文件“/usr/lib/locale/locale-archive”和“/usr/lib/gconv/gconv-modules.cache”。

它可以使用strace实用程序显示。这两个文件都来自 glibc。

于 2013-01-09T13:44:47.207 回答
1

原因是它需要对当前语言环境进行 Unicode 感知比较,从 Marat 的回答来看,这样做效率不高。

这显示了不考虑 Unicode 时它的速度有多快:

$ time LC_CTYPE=C grep -i fun test.txt
all work and no plJack is no fun
Jack is no Fun
real    0m0.192s

当然,这种替代方法不适用于其他语言中的字符,例如 Ñ/ñ、Ø/ø、Ð/ð、Æ/æ 等。

另一种选择是修改正则表达式,使其不区分大小写:

$ time grep '[Ff][Uu][Nn]' test.txt
all work and no plJack is no fun
Jack is no Fun
real    0m0.193s

sh这是相当快的,但是将每个字符转换为一个类当然很痛苦,并且与上面的不同,将它转换为别名或脚本并不容易。

为了比较,在我的系统中:

$ time grep fun test.txt
all work and no plJack is no fun
real    0m0.085s

$ time grep -i fun test.txt
all work and no plJack is no fun
Jack is no Fun
real    0m3.810s
于 2016-11-25T18:16:15.660 回答
0

要进行不区分大小写的搜索,grep 首先必须将整个 50 MB 文件转换为一种或另一种。这需要时间。不仅如此,还有内存副本……

在您的测试用例中,您首先生成文件。这意味着它将被内存缓存。第一次运行 grep 只需要映射缓存的页面;它甚至不必访问磁盘。

不区分大小写的 grep 执行相同的操作,但随后它会尝试修改该数据。这意味着内核将对每个修改的 4 kB 页面进行异常处理,最终不得不将整个 50 MB 复制到新内存中,一次一页。

基本上,我希望这会更慢。也许不是慢 57 倍,但肯定更慢。

于 2012-12-11T12:12:02.260 回答