为什么"grep --ignore-case"慢 50 倍?



我非常惊讶地发现,当您将--ignore-case选项添加到grep时,它可以将搜索速度降低50倍。我在两台不同的机器上进行了测试,结果相同。我很想找到一个巨大性能差异的解释。

我还希望看到一个grep的替代命令,用于不区分大小写的搜索。我不需要正则表达式,只需要修复字符串搜索。首先,测试文件将是一个50;MB纯文本文件和一些伪数据,您可以使用以下代码来生成它:

创建测试.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选项,该命令将变慢57x倍。

$ 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

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

更新-Cents

上面测试的两台机器都运行Ubuntu,一台11.04(Natty Narwhal),另一台12.04(Precise Pangolin)。在CentOS 5.3计算机上运行相同的测试会产生以下有趣的结果。这两种情况的性能结果几乎相同。现在,2009年1月发布了CentOS 5.3,运行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

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

错误报告grep,忽略案例速度慢

这种缓慢是由于grep(在UTF-8语言环境中)不断访问文件"/usr/lib/loce/loce-archive"one_answers"/usr/lib/gconv/gconv modules.cache"。

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

原因是它需要对当前区域设置进行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

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

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

不区分大小写的grep也会这样做,但随后它会尝试修改该数据。这意味着内核将为每个修改的4 kB页面,并且最终将不得不复制整个50;MB存储到新内存中,每次一页。

基本上,我预计这会更慢。也许不会慢57倍,但肯定会慢一些。

相关内容

  • 没有找到相关文章

最新更新