【问题标题】:Very slow loop using grep or fgrep on large datasets在大型数据集上使用 grep 或 fgrep 的循环非常慢
【发布时间】:2012-12-18 01:35:39
【问题描述】:

我正在尝试做一些非常简单的事情;在目录中的文件上从列表中 grep,字符串的完全匹配:

#try grep each line from the files
for i in $(cat /data/datafile); do 
LOOK=$(echo $i);
fgrep -r $LOOK /data/filestosearch >>/data/output.txt
done

与 grep 匹配的文件有 2000 万行,目录有 ~600 个文件,总共 ~4000 万行 我可以看到这会很慢,但我们估计需要 7 年。即使我在我们的 HPC 上使用 300 个内核将作业按文件拆分以进行搜索,看起来也可能需要一周多的时间。

有类似的问题:

Loop Running VERY Slow :

Very slow foreach loop

在这里,尽管它们位于不同的平台上,但我认为如果有其他可能对我有帮助的话。 或 fgrep 可能更快(但似乎有点慢,因为我现在正在测试它) 谁能看到更快的方法来做到这一点? 提前谢谢你

【问题讨论】:

  • 使用fgrep --word-regexp,除非你需要子串匹配。如果您只想知道匹配的文件名,也可以尝试fgrep --files-with-matches。
  • 由于i 已经是一个变量,因此无需生成一个子shell 来将其值分配给另一个名为LOOK 的变量。
  • 顺便说一句,不必要的和不雅的反引号也会导致减速,尽管可能不超过总处理时间的百分之几。 partmaps.org/era/unix/award.html#backticks
  • @tripleee,我看不到反引号,你的意思是什么?
  • 抱歉,我不准确地指代 $(command)(此构造的旧 Bourne 语法使用反引号)。

标签: bash loops grep


【解决方案1】:

您可以编写 perl/python 脚本,它会为您完成这项工作。当您使用外部工具执行此操作时,它会节省您需要执行的所有分叉。

另一个提示:您可以在一个正则表达式中组合您正在寻找的字符串。 在这种情况下,grep 只会对所有组合行执行一次。

例子:

代替

for i in ABC DEF GHI JKL
do
grep $i file >> results
done

你可以的

egrep "ABC|DEF|GHI|JKL" file >> results

【讨论】:

  • hmm,我认为放入 20M 匹配会有点长,但我可以将它包装在一个脚本中,我想,可以试试这个,谢谢你的回复
【解决方案2】:

听起来grep 的-f 标志在这里很合适:

-f FILE, --file=FILE
    Obtain  patterns  from  FILE,  one  per  line.   The  empty file
    contains zero patterns, and therefore matches nothing.   (-f  is
    specified by POSIX.)

所以grep 已经可以执行您的循环正在执行的操作,您可以将循环替换为:

grep -F -r -f /data/datafile /data/filestosearch >>/data/output.txt

现在我不确定 2000 万个模式的性能,但至少您不会以这种方式启动 2000 万个进程,因此它可能会快得多。

【讨论】:

  • 对不起,我的意思是说我先使用 grep -f,然后我看到 fgrep 更快,我可能会编辑原文以提及这一点。我现在从您的回答中看到,实际上它们需要结合使用>不,我很困惑。看着我的剧本,我正在重新编辑它原来的样子。再次抱歉
  • 是的,fgrep 表示grep -F(大写 F),因为它将模式视为固定字符串而不是正则表达式,所以速度稍快。我建议您还添加-f(小写f)以从文件中加载模式。这样你就可以消除grep的启动开销
【解决方案3】:

正如 Martin 在他的回答中已经说过的那样,您应该使用 -f 选项而不是循环。我认为它应该比循环更快。

此外,这看起来像是 GNU parallel 的一个极好的用例。查看this answer 以获取使用示例。它看起来很困难,但实际上设置和运行很容易。

除此之外,如果只有一个字符串要匹配,4000 万行对 grep 来说应该不是什么大问题。它应该能够在一两分钟内在任何体面的机器上完成。我在笔记本电脑上测试了 200 万行需要 6 秒。所以 4000 万行应该需要 2 分钟。

问题在于有 2000 万个字符串要匹配。我认为它一定是内存不足或什么的,特别是当你在不同的目录上运行它的多个实例时。您可以尝试拆分输入匹配列表文件吗?例如,尝试将其分成 100000 个单词的块。

编辑:刚刚在我的机器上尝试并行。这是惊人的。它会自动将 grep 拆分到几个核心和几台机器上。

【讨论】:

  • 试试:parallel -j2 'cat datafile |并行 --pipe grep -F -f - ' ::: files_to_search*
  • 或:parallel -n30 -j2 'cat 数据文件 |并行 --pipe grep -F -f - ' ::: files_to_search*
  • 感谢您的回答,我将尝试其中一些解决方案。
【解决方案4】:

这是加快速度的一种方法:

while read i
do
    LOOK=$(echo $i)
    fgrep -r $LOOK /deta/filetosearch >> /data/output.txt
done < /data/datafile

当您这样做for i in $(cat /data/datafile) 时,您首先会生成另一个进程,但该进程必须在运行脚本的其余部分之前找出所有这些行。此外,您很有可能会超载命令行并最终丢失一些文件。

通过使用 q while read 循环并重定向来自 /data/datafile 的输入,您无需生成 shell。此外,您的脚本将立即开始阅读 while 循环,而无需先找出整个 /data/datafile。

如果$i是一个目录列表,并且你对下面的文件感兴趣,我想知道find会不会比fgrep -r快一点。

在阅读时 做 看=$(回声$i) 查找 $i -type f | xargs fgrep $LOOK >> /data/output.txt 完成/datafile

xargs 将获取 find 的输出,并在单个 fgrep 下运行尽可能多的文件。如果这些目录中的文件名包含空格或其他奇怪字符,xargs 可能会很危险。您可以尝试(取决于系统),如下所示:

find $i -type f -print0 | xargs --null fgrep $LOOK >> /data/output.txt

在 Mac 上是

find $i -type f -print0 | xargs -0 fgrep $LOOK >> /data/output.txt

正如其他人所说,如果你有 GNU 版本的 grep,你可以给它 -f 标志并包含你的 /data/datafile。然后,就可以彻底消除循环了。

另一种可能性是切换到 Perl 或 Python,它们实际上会比 shell 运行得更快,并为您提供更多的灵活性。

【讨论】:

  • 啊,谢谢你的详细解释,这让我更清楚了。 xargs 应该可以工作我没有空格。好的,我会尝试但这次不使用 python。我从未使用过 python,但我想我会尝试学习一些我的 perl 甚至比我的 bash 知识更不稳定,但它应该足够简单。
  • 我也需要文件中的行,而不是位置,所以我认为 find 没那么有用。谢谢
  • $(echo) 看起来仍然完全是多余和浪费的。
  • 在每个文件中使用find 运行fgrep 确实会提取匹配的行,而不是文件名(我想这就是您所说的“位置”)。
  • xargs -0 也应该可以在其他平台上工作,至少是那些使用 GNU find 的平台。
【解决方案5】:

由于您正在搜索简单的字符串(而不是正则表达式),您可能希望使用comm:

comm -12 <(sort find_this) <(sort in_this.*) > /data/output.txt

它占用的内存非常少,而 grep -f find_this 可以吞噬 'find_this' 大小的 100 倍。

在 8 核上,这些文件需要 100 秒:

$ wc find_this; cat in_this.* | wc
3637371   4877980 307366868 find_this
16000000 20000000 1025893685

确保拥有一个相当新的sort 版本。它应该支持--parallel。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-07
    • 2012-03-31
    • 1970-01-01
    • 2016-10-25
    • 1970-01-01
    相关资源
    最近更新 更多