这是我自己的实验性并行版本的grep,因此我可以测试各种策略来加速对大型目录树的访问。在闪存或 SSD 上,您可以轻松地比常见的 grep 聪明 8 倍。
选项:
Usage: ./greppin [-rIOLlsSH] [-n <cores>] <regex> <path>
-2 -- use PCRE2 instead of PCRE
-O -- print file offset of match
-l -- do not print the matching line (Useful if you want
to see _all_ offsets; if you also print the line, only
the first match in the line counts)
-s -- single match; dont search file further after first match
(similar to grep on a binary)
-H -- use hyperscan lib for scanning
-S -- only for hyperscan: interpret pattern as string literal instead of regex
-L -- machine has low mem; half chunk-size (default 2GB)
may be used multiple times
-I -- enable highlighting of matches (useful)
-n -- Use multiple cores in parallel (omit for single core)
-r -- recurse on directory
scrap使用PCRE库,因此基本上相当于grep -P -a
。 -P
很重要,因为 Perl 兼容正则表达式与基本正则表达式具有不同的特征。
有两个分支。 master
和greppin
。 Master 是“传统”的抓取工具,应该在大多数 POSIX 系统上编译和运行。 greppin
附带了自己优化和并行化的nftw()
和readdir()
版本,这在master
分支已经提供的加速基础上再次将速度提高了一倍。 greppin
分支在 Linux、BSD 和 OSX 上运行。 greppin
还支持 Intel 的 hyperscan 库,在将正则表达式模式编译为 JIT 代码时,如果可能的话,这些库会尝试利用 CPU 的 SIMD 指令(AVX2、AVX512 等)。
您很可能想要构建greppin
分支:
$ git checkout greppin
[...]
$ cd src; make
[...]
确保您已安装pcre和pcre2库包。在 BSD 系统上,您需要gmake
而不是make
。如果您想使用greppin 的多个正则表达式引擎和 hyperscan 支持来实现尖端技术,您首先需要获取并构建:
$ git clone https://github.com/intel/hyperscan
[...]
$ cd hyperscan
$ mkdir build; cd build
$ cmake -DFAT_RUNTIME=1 -DBUILD_STATIC_AND_SHARED=1 ..
[...]
$ make
[...]
这将构建所谓的 hyperscan 库的胖运行时,其中包含对所有 CPU 系列的支持,以便在运行时选择正确的编译模式以获得最佳性能。构建完成后,您可以针对该构建greppin :
(在抓取克隆仓库内)
$ cd src
$ HYPERSCAN_BUILD=/path/to/hyperscan/build make -f Makefile.hs
[...]
这将生成一个greppin
二进制文件,使-H
选项能够在运行时加载不同的引擎,尝试利用所有可能的性能位。
您可以将其链接到已安装的库,但该 API 最近刚刚在 5.x 版本中添加了一些功能,并且大多数发行版都附带 4.x。
scrap使用mmap(2)
并匹配整个文件 blob,而不计算换行符(即使没有匹配, grep也会这样做 [截至我 2012 年的 grep 代码审查;今天的情况可能有所不同]),这是很多比read(2)
更快 - 将文件分成小块并计算换行符。如果可用, grab还使用 PCRE JIT 功能。然而,加速只能在大型文件树或快速 HDD 或 SSD 上进行测量。在后一种情况下,如果递归且并行匹配,加速效果可能会非常显着(最多快 3 倍)。由于存储是瓶颈,因此在 HDD 上并行搜索是没有意义的,因为搜索比仅以线性方式执行操作需要更多时间。
此外, grab会跳过太小而无法包含正则表达式的文件。对于递归搜索中较大的正则表达式,这可以跳过大量文件,甚至无需打开它们。
需要一个相当新的pcre库,在某些较旧的系统上,构建可能会由于缺少PCRE_INFO_MINLENGTH
和pcre_study()
而失败。
文件以 1Gig 的块进行映射和匹配。对于较大的文件,块的最后 4096 字节(1 页)会重叠,以便可以找到 1 Gig 边界上的匹配项。在这种情况下,您会看到匹配加倍(但偏移量相同)。
如果您测量grep与grab ,请记住在每次运行之间删除目录项和页面缓存: echo 3 > /proc/sys/vm/drop_caches
请注意,如果grep检测到二进制文件,则它只会打印“二进制文件匹配”,而grab将打印所有匹配,除非给出-s
。因此,对于速度测试,您必须搜索数据中不存在的表达式,以便强制搜索整个文件。
抓取是为了在大型目录树中快速 grep 而不需要索引。迄今为止,原始grep具有更完整的选项集。单个文件匹配的加速即使可以测量,也非常小。
对于 SSD,多核选项很有意义。对于 HDD 来说则不然,因为磁头必须在线程之间来回定位,这可能会破坏局部性原则并降低性能。
greppin
分支有自己的无锁并行版本nftw()
,因此第一个核心构建目录树时 N - 1 个核心的空闲时间也可以用于工作。
还需要注意的是: grab将物理遍历目录,即它不会遵循符号链接。
spot
是find
的并行版本。正如您所知,它支持最常用的选项。没什么可说的,只是尝试一下。
这显示了在 SSD 上进行搜索时 4 核机器上的加速情况:
root@linux:~# echo 3 > /proc/sys/vm/drop_caches
root@linux:~# time grep -r foobardoesnotexist /source/linux
real 0m34.811s
user 0m3.710s
sys 0m10.936s
root@linux:~# echo 3 > /proc/sys/vm/drop_caches
root@linux:~# time grab -r foobardoesnotexist /source/linux
real 0m31.629s
user 0m4.984s
sys 0m8.690s
root@linux:~# echo 3 > /proc/sys/vm/drop_caches
root@linux:~# time grab -n 2 -r foobardoesnotexist /source/linux
real 0m15.203s
user 0m3.689s
sys 0m4.665s
root@linux:~# echo 3 > /proc/sys/vm/drop_caches
root@linux:~# time grab -n 4 -r foobardoesnotexist /source/linux
real 0m13.135s
user 0m4.023s
sys 0m5.581s
使用greppin
分支:
root@linux:~# echo 3 > /proc/sys/vm/drop_caches
root@linux:~# time grep -a -P -r linus /source/linux/|wc -l
16918
real 1m12.470s
user 0m49.548s
sys 0m6.162s
root@linux:~# echo 3 > /proc/sys/vm/drop_caches
root@linux:~# time greppin -n 4 -r linus /source/linux/|wc -l
16918
real 0m8.773s
user 0m4.670s
sys 0m5.837s
root@linux:~#
是的! ~ 9 秒 vs. ~ 72 秒!在 4 核 SSD 机器上,速度是传统 grep 的 8 倍。
只是为了证明它会产生相同的输出:
root@linux:~# echo 3 > /proc/sys/vm/drop_caches
root@linux:~# greppin -n 4 -r linus /source/linux/|sort|md5sum
a1f9fe635bd22575a4cce851e79d26a0 -
root@linux:~# echo 3 > /proc/sys/vm/drop_caches
root@linux:~# grep -P -a -r linus /source/linux/|sort|md5sum
a1f9fe635bd22575a4cce851e79d26a0 -
root@linux:~#
在单核比较中,加速还取决于内核实际调度grep的 CPU,因此抓取可能会更快,也可能不会更快(大多数情况下是这样)。如果单核测试中的负载相等,则在大型文件树上搜索时, grab会看到加速。在多核设置上, grab可以受益于corse。
该项目可以在这里找到。
其基准表中的主要加速源于以下事实: ripgrep在没有特殊选项的情况下调用时会忽略许多文件(尤其是点文件),并将二进制文件视为单匹配目标(类似于grep )。为了获得可比较的结果,请记住(4 是核心数量):
echo 3 > /proc/sys/vm/drop_caches
-j 4 -a --no-unicode --no-pcre2-unicode -uuu --mmap
添加到ripgrep ,因为它默认匹配慢 3 倍的 Unicode,并尝试通过跳过 'ignore' 来补偿速度损失基于文件。 -e
比-P
更快,所以最好选择-e
,但这不如 PCRE 强大/dev/null
以避免基于 tty 的影响-H -n 4
添加到greppin中。 -H
与 PCRE 兼容,只有极少数例外(根据 hyperscan 文档)setfattr -n user.pax.flags -v "m" /path/to/binary
如果您在 grsec 系统上运行并且需要 rwx JIT 映射然后继续检查时间。即使不使用 hyperscan,在使用 PCRE2 表达式(PCRE2 与 PCRE2)时, greppin
也明显快于rg
并且在比较最快的表达式(-e 与 hyperscan)时仍然更快。