whileループでgrepコマンドを使用するときに並列性を使用する方法

whileループでgrepコマンドを使用するときに並列性を使用する方法

すべての検索文字列を含むファイルがあります。そのファイルからすべての文字列を取得し、1つずつ別のファイルに保存しています。今は時間がかかりすぎます。このファイルで並列コマンドをどのように実装できますか?

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

CPU、RAM、ディスクI/Oという3つの制限要因があります。

RAMは測定が簡単です。 grepプロセスが利用可能なメモリの大部分を占める場合(たとえば、トップ実行時)、RAMは制限要因です。

CPUは測定も簡単です。 grepがCPUの90%を超える場合、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

これにより、約80%少ないメモリを使用し、より高速のgrep -Fを使用できます。

それでもメモリに収まらない場合は、次のことができます。

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ごとに1回大きなファイルを読み取ります。ただし、これは並行して実行されるため、最初の読み取り以外のすべての読み取りはRAMにキャッシュされます。 regexp.txtのサイズによっては、-L1000の代わりに--block 10mを使用する方が速いかもしれません。

一部のストレージシステムは、複数のブロックを並列に読み取る場合にパフォーマンスが向上します。これは、いくつかのRAIDシステムといくつかのネットワークファイルシステムに当てはまります。大容量ファイルを並列に読み取る:

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

これにより、ビッグファイルが100 MBのチャンクに分割され、各チャンクに対してgrepが実行されます。 bigfile と regexp.txt を並列に読み取るには、--fifo を使って 2 つを結合します。

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

複数の正規表現と一致すると、1行が重複する可能性があります。

大きな問題

問題が大きすぎて解決できない場合は、おそらくLuceneを使用する準備が整いました。

答え2

あなたはGNUの並列性について聞いたことがあります。ここでは動作しません...

並列化を利用するには巨大なファイルでなければなりませんが、bashはそうすることはできません。 Cまたは他の実際のプログラミング言語に切り替える必要があります。

あなたのコードは以下を満たす必要があります。

  • ファイルの長さLの決定
  • Xプロセスでフォーク
  • それらはすべてファイルのn * X_nビットから読み始める必要があります。
  • L/Xビットを読み込んだ後は停止する必要があります
  • 読んだセクションを確認してください。
  • IPCを使用したendfileの同期書き込み

これは確かに可能ですが、このオプションを考慮する価値がある場合は、ファイルの長さが数テラバイトでなければなりません。

関連情報