我对外壳中GREP的功能感到非常惊讶,之前我曾经在Java中使用子字符串方法,但是现在我使用GREP并在几秒钟内执行,它比我以前编写的Java代码快得多。 (根据我的经验,我可能是错的)
话虽这么说,我还无法弄清楚这是怎么回事?网络上也没有太多可用的内容。
谁能帮我这个?
我对外壳中GREP的功能感到非常惊讶,之前我曾经在Java中使用子字符串方法,但是现在我使用GREP并在几秒钟内执行,它比我以前编写的Java代码快得多。 (根据我的经验,我可能是错的)
话虽这么说,我还无法弄清楚这是怎么回事?网络上也没有太多可用的内容。
谁能帮我这个?
Answers:
假设您的问题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禁用此优化。)
该答案是从此处获取的信息的子集。
添加到史蒂夫的出色答案。
它可能并不广为人知,但是当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
比更快grep
。f
in fgrep
并不代表“快速”,它代表“固定”(请参见手册页),并且由于两者都是同一程序,并且都使用Boyer-Moore,因此在搜索固定-没有regexp特殊字符的字符串。唯一的原因,我使用的fgrep
是当有一个正则表达式特殊字符(如.
,[]
或*
)我不希望它被解释为这样的。即使这样,也更grep -F
偏爱于便携式/标准形式的fgrep
。
xs.txt
包含100000000'x',而您确实这样做了grep yx xs.txt
,那么它实际上比比您更早地找不到匹配项grep yxxxxxxxxxxxxxxxxxxx xs.txt
。在这种情况下,Boyer-Moore-Horspool对Boyer-Moore的改进在向前跳过时有所改进,但是在一般情况下,它可能不会仅仅是三个机器指令。
grep/fgrep/egrep
成为同一可执行文件的所有硬链接的日子已经一去不复返了。它们(以及其他z*grep
bz*grep
可动态解压缩的扩展程序(如utils))现在是周围的小型外壳包装器grep
。在此提交中可以找到有关单个可执行文件和外壳程序包装之间切换的一些有趣的历史注释:git.savannah.gnu.org/cgit/grep.git/commit/…–