如何在终端中启动多线程grep?

如何在终端中启动多线程grep?

我有一个文件夹,其中包含 250 多个文件,每个文件大小为 2 GB。我需要在这些文件中搜索字符串/模式并将结果输出到文件中output。我知道我可以运行以下命令,但它太慢了!

grep mypattern * > output

我想加快速度。作为一名 Java 程序员,我知道多线程可以用来加速进程。我陷入了如何grep以“多线程模式”启动并将输出写入单个output文件的困境。

答案1

有两个简单的解决方案。基本上,使用xargsparallel.

xargs 方法:

您可以xargsfind如下方式使用:

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 和积极的文字优化来使搜索速度非常快。”

最后,还有其他方面rggrep.根据您的情况,您可能需要关闭其中一些功能:

  • 默认情况下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,但我认为这可能仍然有用。

相关内容