如何在 while 循环中使用 grep 命令时使用并行

如何在 while 循环中使用 grep 命令时使用并行

我有一个包含所有搜索字符串的文件,我正在从该文件中取出所有字符串,并将它们一一地放在另一个文件中,现在这花费了太长时间,我如何在其中实现并行命令。

while read line; do
line2=`grep -w "$line" $file2`

if [[ ! -z $line2 ]]
then
   echo "$line: present" >> exclusion_list_$$.txt
   echo "$line2" >> exclusion_list_$$.txt

   echo "grep line $line2 "
fi
done < exclusion.txt 

我在想也许可以将所有内部 while 命令放入一个函数中并并行调用该函数。

我对此很陌生,请告诉我这是否是正确的方法,或者是否有其他有效的方法。

答案1

看来你的问题是在 https://www.gnu.org/software/parallel/man.html#示例:-Grepping-n-lines-for-m-regular-expressions

示例:在 n 行中查找 m 正则表达式。

grep 大量正则表达式的大文件的最简单解决方案是:

grep -f regexps.txt bigfile

或者如果正则表达式是固定字符串:

grep -F -f regexps.txt bigfile

有 3 个限制因素:CPU、RAM 和磁盘 I/O。

RAM 很容易测量:如果 grep 进程占用了大部分可用内存(例如运行 top 时),那么 RAM 就是一个限制因素。

CPU 也很容易测量:如果 grep 在 top 中占用 >90% 的 CPU,那么 CPU 是一个限制因素,并行化将加快这一速度。

很难看出磁盘 I/O 是否是限制因素,并且根据磁盘系统,并行化可能会更快或更慢。唯一确定的方法是测试和测量。

限制因素:RAM

无论大文件的大小如何,正常的 grep -f regexs.txt 大文件都可以工作,但是如果 regexps.txt 太大而无法放入内存,那么您需要将其拆分。

grep -F 大约需要 100 字节的 RAM,而 grep 每 1 字节的正则表达式大约需要 500 字节的 RAM。因此,如果 regexps.txt 占 RAM 的 1%,那么它可能太大了。

如果您可以将正则表达式转换为固定字符串,请执行此操作。例如,如果您在大文件中查找的行全部如下所示:

ID1 foo bar baz Identifier1 quux
fubar ID2 foo bar baz Identifier2

那么你的 regexps.txt 可以从以下内容转换:

ID1.*Identifier1
ID2.*Identifier2

进入:

ID1 foo bar baz Identifier1
ID2 foo bar baz Identifier2

这样,您可以使用 grep -F,它占用的内存减少了大约 80%,而且速度更快。

如果它仍然不适合内存,您可以这样做:

parallel --pipepart -a regexps.txt --block 1M grep -Ff - -n bigfile | \
  sort -un | perl -pe 's/^\d+://'

1M 应该是您的可用内存除以 CPU 线程数,对于 grep -F 则除以 200,对于普通 grep 除以 1000。在 GNU/Linux 上你可以这样做:

free=$(awk '/^((Swap)?Cached|MemFree|Buffers):/ { sum += $2 }
           END { print sum }' /proc/meminfo)
percpu=$((free / 200 / $(parallel --number-of-threads)))k

parallel --pipepart -a regexps.txt --block $percpu --compress \
  grep -F -f - -n bigfile | \
  sort -un | perl -pe 's/^\d+://'

如果您可以忍受重复的行和错误的顺序,那么执行以下操作会更快:

parallel --pipepart -a regexps.txt --block $percpu --compress \
  grep -F -f - bigfile

限制因素:CPU

如果 CPU 是限制因素,则应在正则表达式上进行并行化:

cat regexp.txt | parallel --pipe -L1000 --roundrobin --compress \
  grep -f - -n bigfile | \
  sort -un | perl -pe 's/^\d+://'

该命令将为每个 CPU 启动一个 grep 并为每个 CPU 读取一次大文件,但由于这是并行完成的,除了第一个读取之外的所有读取都将缓存在 RAM 中。根据 regexp.txt 的大小,使用 --block 10m 而不是 -L1000 可能会更快。

一些存储系统在并行读取多个块时性能更好。对于某些 RAID 系统和某些网络文件系统来说确实如此。并行读取大文件:

parallel --pipepart --block 100M -a bigfile -k --compress \
  grep -f regexp.txt

这会将 bigfile 分割成 100MB 的块,并对每个块运行 grep。要并行读取 bigfile 和 regexp.txt,请使用 --fifo 将两者结合起来:

parallel --pipepart --block 100M -a bigfile --fifo cat regexp.txt \
  \| parallel --pipe -L1000 --roundrobin grep -f - {}

如果一行与多个正则表达式匹配,则该行可能会重复。

更大的问题

如果问题太大而无法解决,那么您可能已经准备好使用 Lucene了。

答案2

您可能听说过 GNU 并行。这在这里行不通...

要利用并行化,它必须是一个巨大的文件,而 bash 无法做到这一点,您必须切换到 C 或其他真正的编程语言。

您的代码必须:

  • 确定文件的长度L
  • 分叉到 X 个进程
  • 它们都必须从文件的 n*X_n 位开始读取
  • 读取 L/X 位后必须停止
  • 检查他们阅读的部分
  • 使用IPC同步写入endfile

这当然是可能的,但文件的长度必须达到几 TB,才值得考虑此选项。

相关内容