複数のファイルでzgrepのパフォーマンスが低下する

複数のファイルでzgrepのパフォーマンスが低下する

9.8GBgzipファイルA.gzと私が持っている他のファイルは次のとおりです。79MBB.txtには各行にいくつかのテキストがあります。 A.gzでBのテキストをgrepして新しいファイルに書きたいです。

最初はこのコマンドを使用しました。

zgrep -f B.txt A.gz > C.xml

しかし、このコマンドは中断され、長い間空のC.xmlを生成します。

その後、インターネット検索後にB.txtが大きいため、テキストをバッファに保持しながら中断されることがわかりました。

そのため、テキストファイルをそれぞれ20000個のテキストに分割しました。

split -l 20000 -a 4 B.txt B

Baaaa, Baaab... 同じファイルを作成しました。

その後、各ファイルを繰り返します。

cd B
for f in B*; do
  zgrep -f "$f" ../A.gz >> C.xml
done

非常に遅く、まだ実行中です。

これを行うより良い方法はありますか?

gzファイルを圧縮するとパフォーマンスが向上しますか?

修正する

-Fを使ってみました。

zgrep -F -f "$f" ../A.gz >> C.xml

少し速いですが、まだ別のオプションが必要です。

私はこのようなXMLを持っています

<root>
   <source>source1</source>
   <Id>123</Id>
   <category>ABC</category>
</root>
<root>
    <source>source2</source>
    <Id>123</Id>
    <category>XYZ</category>
</root>

ここでIDは123と同じですが、カテゴリはABCとXYZが異なります。

(入力はABC、DEF、GHI、JKLM、NOPなどの制限されたカテゴリのセットです。)最初に私のカテゴリはABCであるため、カテゴリABCに基づいてそのIDが123であることを見つけて、属するすべてのIDを作成し続けます。カテゴリは、次のように新しいファイル、つまりB.txt(IDリスト)に入力されます。

zgrep -E 'ABC|DEF|GHI|JKLM|NOP' A.gz | sed -n 's:.*<Id>\(.*\)</Id>.*:\1:p' | uniq > B.txt

後でこのIDを繰り返し、すべてのxmlを取得し、ID 123のカテゴリABCとXYZに属するxmlタグを取得します。

答え1

79MB grep "string"は作業が難しいでしょう。この行はB.txt正規表現ですか、それとも同じ固定文字列ですか?固定文字列の場合、A.gz行全体で同じように表示されますか? Uncompressedの行数はA.gzの行と一致すると予想されますかB.txt

パターンマッチ提案

の行がB.txt実際に正規表現であるか、行の部分文字列である場合は、次のようなものをA.gz使用する必要があります。ハイパースキャン巨大な正規表現を処理するように設計されています。十分なディスク容量がある場合は、解凍してA.gzHyperScanを動作させることができます(HyperScanが検索中にシェルにすぐに解凍することもできます)。試してみるもう1つの選択肢は次のとおりです。リップグレップ

フルラインマッチング提案

固定された行文字列全体を処理B.txtし、圧縮されていない文字列にA.gz比較的小さい(たとえば100 MB程度)一致する行が含まれている場合は、前処理プログラムを作成する方が良いかもしれませんA.gz

  • 各行をハッシュB.txtしてハッシュを覚えます。
  • 次に、非圧縮ハッシュの行が前のハッシュA.gzと同じであることを確認します。そのC.txt場合は、追加の処理を準備するためにその行を印刷します(例:Enter)。
  • ここで最後のチェックを行います。各行がその中にあるかどうかをB.txtより厳密に調べますC.txt(またはその逆 - どのファイルが小さいかによって異なります)。

初期近似フィルタリングを実行するいくつかのコードは次のとおりです。

# Do a quick APPROXIMATE filter of lines in FILENEEDLES that are also in
# FILEHAYSTACK
import sys

def main():
    if len(sys.argv) < 2:
        print("usage: %s FILENEEDLES FILEHAYSTACK" % sys.argv[0])
        exit(1)

    first_filename = sys.argv[1]
    second_filename = sys.argv[2]

    line_hashes = set()

    with open(first_filename, "r") as f:
        for line in f:
            line_hashes.add(hash(line))

    with open(second_filename, "r") as f:
        for line in f:
            if hash(line) in line_hashes:
                sys.stdout.write(line)

if __name__ == "__main__":
    main()

たとえば、

$ echo -e '1\n2\n3' > B.txt
$ echo -e '2\n3\n4\5' | gzip > A.gz
$ ./approxfilter.py B.txt <(gzip -dc A.gz) > candidates.txt
$ cat candidates.txt
2
3

これで、行の出力が正確に一致することを確認するためにCandidate.txtをチェックする必要がありますB.txt(しかし、これはより小さくて簡単な問題であることを願っています。アーカイブ可能な範囲内で)(質問者は、後でコメントに完全な行長文字列を使用しないため、このアプローチが機能しないことを明確にしました)。

答え2

2番目の試みは解凍によって改善される可能性が高いです。それ以外の場合は、ループを繰り返すたびに完全な解凍オーバーヘッドが発生します。事前に解凍すると、そのオーバーヘッドが一度だけ発生することを意味します。

それでも速度が十分に速くない場合は、マルチスレッドを試すこともできます(Aが解凍されたと仮定)。

find B -type f -name 'B*' -print0 \
  | xargs -0 -t -n1 -P8 \
  grep -f {} A >> C.xml

この例では、8つのプロセスを同時に実行する必要があり、保持しているプロセッサ/コアの数に応じてこの値を調整する必要があります。

予想される速度の結果が何であるかよくわかりません。率直に言って、私は多くのことを行い、時間がかかるように聞こえます。

答え3

zgrepただシェルスクリプトラッパーですgrep。単にgrepシステムにインストールされているすべてを実行し、それを使用してgzip入力ファイルを解凍します。

GNU grep バージョン 3.5 または 3.6 を使用している場合、パターン ファイルの処理速度が遅くなり、説明したように極端なパフォーマンスの低下を引き起こすバグが最近発見されました。

grep 3.7リリースノートのバグを含むサンプルパターンファイルは約48Mbのパターンなので、私が知っている限りサイズは問題ありません。

https://www.theregister.com/2021/08/16/gnu_grep_37/

関連情報