grep如何运行这么快?


113

我对外壳中GREP的功能感到非常惊讶,之前我曾经在Java中使用子字符串方法,但是现在我使用GREP并在几秒钟内执行,它比我以前编写的Java代码快得多。 (根据我的经验,我可能是错的)

话虽这么说,我还无法弄清楚这是怎么回事?网络上也没有太多可用的内容。

谁能帮我这个?


5
它是开源的,因此您可以自己看看。gnu.org/software/grep/devel.html
driis

6
可笑的鱼有一个很好的文章,准确地回答了您的问题:ridiculousfish.com/blog/posts/old-age-and-treachery.html
David Wolever 2012年

@WilliamPursell当执行时间以秒为单位时,JIT可能已经预热了,令人麻木的差异是由于(1)grep对其执行的功能非常聪明,以及(2)Java代码做出了非常糟糕的算法选择对于grep关注的特定问题。

3
您的Java实现花费了多少时间来启动JVM,以及它实际上花费了多少时间来执行代码?或者可能与您在Java代码中使用的算法有关;O(N ^ 2)算法在任何语言中都可能很慢。
基思·汤普森

Answers:


169

假设您的问题GNU grep具体针对。这是作者Mike Haertel的注释:

GNU grep速度很快,因为它避免了每个输入字节的查找。

GNU grep的是快,因为它执行非常少的指令,每一个字节,它 看。

GNU grep使用著名的Boyer-Moore算法,该算法首先查找目标字符串的最后一个字母,并使用查找表告诉它,只要找到不匹配的字符,它可以在输入中跳过多远。

GNU grep还展开了Boyer-Moore的内部循环,并以不需要在每个展开的步骤都进行循环退出测试的方式来设置Boyer-Moore增量表条目。这样做的结果是,在限制范围内,GNU grep平均为它实际查看的每个输入字节执行的少于3条x86指令(并且完全跳过了许多字节)。

GNU grep使用原始Unix输入系统调用,并避免在读取数据后复制数据。此外,GNU grep避免将输入断开。寻找换行符会使grep降低数倍,因为要找到换行符,它必须查看每个字节!

因此,GNU grep不会使用面向行的输入,而是将原始数据读取到大缓冲区中,使用Boyer-Moore搜索缓冲区,只有找到匹配项后,它才会寻找边界换行符(某些命令行选项- n禁用此优化。)

该答案是从此处获取的信息的子集。


41

添加到史蒂夫的出色答案。

它可能并不广为人知,但是当grep 较长的模式字符串比短的模式字符串时,grep几乎总是更快,因为在较长的模式中,Boyer-Moore可以向前跨更长的步长以达到更好的亚线性速度:

例:

# after running these twice to ensure apples-to-apples comparison
# (everything is in the buffer cache) 

$ time grep -c 'tg=f_c' 20140910.log
28
0.168u 0.068s 0:00.26

$ time grep -c ' /cc/merchant.json tg=f_c' 20140910.log
28
0.100u 0.056s 0:00.17

较长的表格快35%!

怎么会?Boyer-Moore从pattern-string构造一个跳转表,每当存在不匹配时,它都会选择最长的跳转(从最后一个字符到第一个字符),然后再将输入中的单个char与该跳转表中的char进行比较。

这是一段解释博耶·摩尔Boyer Moore的视频(归功于kommradHomer)

另一个常见的误解(对于GNU grep)fgrep比更快grepfin fgrep并不代表“快速”,它代表“固定”(请参见手册页),并且由于两者都是同一程序,并且都使用Boyer-Moore,因此在搜索固定-没有regexp特殊字符的字符串。唯一的原因,我使用的fgrep是当有一个正则表达式特殊字符(如.[]*)我不希望它被解释为这样的。即使这样,也更grep -F偏爱于便携式/标准形式的fgrep


3
很直观,较长的模式会更快。如果模式是一个字节,则grep必须检查每个字节。如果模式为4字节,则可能会跳过4字节。如果模式与文本一样长,则grep只会执行一步。
2014年

12
是的,这很直观-如果您了解Boyer-Moore的工作原理。
arielf 2014年

2
即便如此,它还是直观的。在干草堆中找到一根长针比在一根短针中更容易
RajatJ

2
“越长越快”的反例是在失败之前必须进行大量测试并且无论如何都无法前进的情况。假设该文件xs.txt包含100000000'x',而您确实这样做了grep yx xs.txt,那么它实际上比比您更早地找不到匹配项grep yxxxxxxxxxxxxxxxxxxx xs.txt。在这种情况下,Boyer-Moore-Horspool对Boyer-Moore的改进在向前跳过时有所改进,但是在一般情况下,它可能不会仅仅是三个机器指令。
lrn

2
@Tino谢谢。是的,似乎(GNU)grep/fgrep/egrep成为同一可执行文件的所有硬链接的日子已经一去不复返了。它们(以及其他z*grep bz*grep可动态解压缩的扩展程序(如utils))现在是周围的小型外壳包装器grep。在此提交中可以找到有关单个可执行文件和外壳程序包装之间切换的一些有趣的历史注释:git.savannah.gnu.org/cgit/grep.git/commit/…–
arielf
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.