我有一个文件夹,其中包含 250 多个文件,每个文件大小为 2 GB。我需要在这些文件中搜索字符串/模式并将结果输出到文件中output
。我知道我可以运行以下命令,但它太慢了!
grep mypattern * > output
我想加快速度。作为一名 Java 程序员,我知道多线程可以用来加速进程。我陷入了如何grep
以“多线程模式”启动并将输出写入单个output
文件的困境。
答案1
有两个简单的解决方案。基本上,使用xargs
或parallel
.
xargs 方法:
您可以xargs
按find
如下方式使用:
find . -type f -print0 | xargs -0 -P number_of_processes grep mypattern > output
您将在其中替换number_of_processes
为要启动的最大进程数。然而,如果您的性能受到 I/O 限制,这并不能保证为您提供显着的性能。在这种情况下,您可能会尝试启动更多进程来补偿等待 I/O 所损失的时间。
此外,通过包含 find,您可以指定更高级的选项,而不仅仅是文件模式,例如修改时间等......
正如 Stéphane 的评论所解释的,这种方法可能存在的一个问题是,如果文件很少,则xargs
可能无法为它们启动足够多的进程。一种解决方案是使用-n
选项来xargs
指定一次应从管道中获取多少个参数。设置-n1
将强制xargs
为每个文件启动一个新进程。如果文件非常大(就像这个问题的情况)并且文件数量相对较少,这可能是理想的行为。但是,如果文件本身很小,启动新进程的开销可能会破坏并行性的优势,在这种情况下,-n
值越大越好。因此,-n
可以根据文件大小和数量对该选项进行微调。
并行方法:
另一种方法是使用 Ole Tange GNU Parallel 工具parallel
(可用这里)。这提供了对并行性的更精细的控制,甚至可以分布在多个主机上(例如,如果您的目录是共享的,这将是有益的)。使用并行的最简单语法是:
find . -type f | parallel -j+1 grep mypattern
其中该选项-j+1
指示并行启动一个超过机器上核心数量的进程(这对于 I/O 有限的任务很有帮助,您甚至可以尝试增加数量)。
xargs
并行还具有实际保留每个进程的输出顺序并生成连续输出的优点。例如,xargs
如果进程 1 生成一行p1L1
,进程 2 生成一行p2L1
,进程 1 生成另一行p1L2
,则输出将是:
p1L1
p2L1
p1L2
而parallel
输出应该是:
p1L1
p1L2
p2L1
这通常比输出更有用xargs
。
答案2
至少有两种方法可以加快 grep CPU 速度:
如果您要搜索固定字符串而不是正则表达式,请指定
-F
标志;如果您的模式仅是 ASCII,请使用 8 位语言环境而不是 UTF-8,例如
LC_ALL=C grep ...
。
如果你的硬盘驱动器是瓶颈,那么这些就无济于事了;在这种情况下,并行化可能也无济于事。
答案3
如果问题不是 I/O 限制,您可以使用针对多核处理进行优化的工具。
您可能想看看 sift (http://sift-tool.org,免责声明:我是这个工具的作者)或白银搜索者(https://github.com/ggreer/the_silver_searcher)。
如果您使用正则表达式模式而不是 spimple 字符串搜索,则 silver 搜索器的文件大小限制为 2GB。
答案4
我意识到这并不能完全回答您的问题,而且可能不适合您。然而,流行的实用程序ripgrep提供rg
默认使用并行性的二进制文件。
如果你想控制并行度,相关标志是-j
。从手册页:
-j, --线程数
要使用的大致线程数。值 0(默认值)会导致 ripgrep 使用启发式方法选择线程计数。
除了并行性之外,引用 ripgrep 自述文件,rg
“它构建在 Rust 的正则表达式引擎之上,[该引擎]使用有限自动机、SIMD 和积极的文字优化来使搜索速度非常快。”
最后,还有其他方面rg
比grep
.根据您的情况,您可能需要关闭其中一些功能:
- 默认情况下
rg
是递归的。
如果您的文件夹包含您不想搜索的子文件夹,您可以使用 禁用递归行为--max-depth 1
。
- 默认情况下,
rg
不搜索任何 .gitignore 或类似文件中存在的文件,也不搜索隐藏文件和二进制文件。
如果您想删除这些过滤器,您可以添加该-u
标志(一次用于忽略文件,两次用于忽略和隐藏文件,三次用于停止所有过滤器)。
总之,从您感兴趣的文件夹中,在最简单的情况下(没有子目录,默认过滤实际上对您的情况有用),您可以运行:
rg mypattern > output
(从文件夹外部,您必须添加其路径:)rg mypattern myfolder > output
。
如果您的文件夹中有子目录并且您想取消默认过滤,则命令将是:
rg --max-depth 1 -uuu mypattern > output
(以及从您的文件夹外部:)rg --max-depth 1 -uuu mypattern myfolder > output
。
虽然这在技术上并不能回答您的问题,因为它不使用grep
,而且它可能不适用于您的情况(例如,如果您不能或不想安装外部实用程序),因为rg
使用并行性并且比 快得多grep
,但我认为这可能仍然有用。