大容量フィルタを使用した大容量ファイルのフィルタリング

大容量フィルタを使用した大容量ファイルのフィルタリング

$file1に保存するために抽出したい$file2

$file1サイズは4 GB、行数は約2000万、サイズ$file2は約140 MB、,2つのファイルの最大行長は1000よりはるかに短いです。文字。LC_ALL=C$file1\0

このコマンドを予期しませんでした

parallel --pipepart -a $file1 grep -Ff $file2

多くのメモリを消費し、オペレーティングシステムによってシャットダウンされます。

スレッド数を制限すると、次のコマンドが機能します。

parallel --pipepart -j 8 -a $file1 grep -Ff $file2

最後のコマンドの場合、htop は各grep -Ff $file2スレッドが 12.3 GB のメモリを消費し続けることを示します。私はこの要件がgrepによって構築された辞書から来たと仮定します$file2

これらのフィルタをより効率的に実装するにはどうすればよいですか?

答え1

覆われているman parallel 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が制限要因であることを確認するのは難しく、ディスクシステムによっては並列化が速くなったり遅くなったりする可能性があります。確かに知ることができる唯一の方法は、テストして測定することです。

制限要素:メモリ

大きなファイルの通常の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 --round-robin --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 --round-robin grep -f - {}

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

大きな問題

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

関連情報