我很惊讶地看到,当您向--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(精确穿山甲)。在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
答案 0 :(得分:8)
我认为这个错误报告有助于理解为什么它很慢:
答案 1 :(得分:8)
这种缓慢是由于grep(在UTF-8语言环境中)不断访问文件" / usr / lib / locale / locale-archive"和" /usr/lib/gconv/gconv-modules.cache"。
可以使用strace实用程序显示它。这两个文件都来自glibc。
答案 2 :(得分: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
答案 3 :(得分:0)
要进行不区分大小写的搜索,grep首先必须将整个50 MB文件转换为一个或另一个。这需要时间。不仅如此,还有记忆副本......
在测试用例中,首先生成文件。这意味着它将被内存缓存。第一个grep运行只需要mmap缓存的页面;它甚至不必访问磁盘。
不区分大小写的grep也会这样做,但之后它会尝试修改该数据。这意味着内核将对每个修改过的4 kB页面采用异常,并且最终必须将整个50 MB复制到新内存中,一次一页。
基本上,我期待这个更慢。也许不是57倍慢,但绝对慢。