bugfix> 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

コマンドの中ですべてのインナーを取り、それらを関数に入れて、関数を並列に呼び出すことを考えていました。

私はこれが初めてなので、これが適切な方法であるかどうか、または他の方法が効率的であるかどうかをお知らせください。

回答 2 件
  • あなたの問題は覆われている問題のようです https://www.gnu.org/software/parallel/man.html#EXAMPLE:-Grepping-n-lines-for-m-regular-expressions

    EXAMPLE: Grepping n lines for m regular expressions.

    The simplest solution to grep a big file for a lot of regexps is:

    
    grep -f regexps.txt bigfile
    
    

    または、正規表現が固定文字列の場合:

    grep -F -f regexps.txt bigfile
    
    

    3つの制限要因があります:CPU、RAM、およびディスクI/O。

    RAMの測定は簡単です:grepプロセスが空きメモリの大部分を占める場合(たとえば、topを実行している場合)、RAMが制限要因になります。

    CPUも簡単に測定できます。grepのCPU使用率が90%を超える場合、CPUが制限要因であり、並列化によってこれが高速化されます。

    ディスクI/Oが制限要因であるかどうかを確認することは困難であり、ディスクシステムによっては、並列化が高速または低速になる場合があります。確実に知る唯一の方法は、テストと測定です。

    制限要因:RAM

    通常のgrep -f regexs.txt bigfileはbigfileのサイズに関係なく機能しますが、regexps.txtが大きすぎてメモリに収まらない場合は、これを分割する必要があります。

    grep -Fは約100バイトのRAMを使用し、grepは1バイトの正規表現あたり約500バイトのRAMを使用します。したがって、regexps.txtがRAMの1%である場合は、大きすぎる可能性があります。

    正規表現を固定文字列に変換できる場合は、それを行います。例えば。 bigfileで探している行がすべて次のようになっている場合:

    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ごとに1つのgrepを開始し、CPUごとに1回ビッグファイルを読み取りますが、それが並行して行われるため、最初の読み取りを除くすべての読み取りがRAMにキャッシュされます。 regexp.txtのサイズによっては、-L1000の代わりに--block 10mを使用する方が速い場合があります。

    一部のストレージシステムは、複数のチャンクを並行して読み取るとパフォーマンスが向上します。これは、一部のRAIDシステムおよび一部のネットワークファイルシステムに当てはまります。 bigfileの読み取りを並列化するには:

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

    これにより、bigfileが100MBのチャンクに分割され、これらのチャンクごとにgrepが実行されます。 bigfileとregexp.txtの両方の読み取りを並列化するには、-fifoを使用して2つを組み合わせます。

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

    行が複数の正規表現に一致する場合、行が重複している可能性があります。

    より大きな問題

    問題が大きすぎて解決できない場合は、おそらくLuceneの準備ができています。

  • あなたはGNU並列について聞いたことがあるかもしれません。これはここでは機能しません...

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

    あなたのコードは:

    ファイルの長さLを決定する

    Xプロセスへの分岐

    それらはすべて、ファイルのn * X_nビットから読み取りを開始する必要があります。

    L/Xビットを読み取った後に停止する必要がある

    彼らが読んだ部分を調べる

    IPCを使用して、endfileへの書き込みを同期します。

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

あなたの答え