我有一个包含所有搜索字符串的文件,我正在从该文件中取出所有字符串,并将它们一一地放在另一个文件中,现在这花费了太长时间,我如何在其中实现并行命令。
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,才值得考虑此选项。