大容量ファイルのパフォーマンス向上

大容量ファイルのパフォーマンス向上

300,000行以上のFILE_Aと30,000行以上のFILE_Bがあります。 FILE_Aの各行をFILE_Bにgrepし、grepの結果を新しいファイルに書き込むbashスクリプトを作成しました。

全体のプロセスは5時間以上かかります。

私のスクリプトのパフォーマンスを向上させる方法があると思うなら、どんな提案でも探しています。

grep コマンドとして grep -F -m 1 を使用します。 FILE_Aは次のとおりです。

123456789 
123455321

FILE_Bは次のようになります。

123456789,123456789,730025400149993,
123455321,123455321,730025400126097,

したがって、bashには、FILE_Aで次の行を選択し、FILE_Bでgrepするwhileループがあります。 FILE_Bでパターンを見つけたら、result.txtに書き込みます。

while read -r line; do
   grep -F -m1 $line 30MFile
done < 300KFile

ご協力ありがとうございます。

答え1

パフォーマンスの鍵は、大容量ファイルを一度だけ読むことです。

複数のパターンを別々の行に配置して grep に渡すことができます。これは通常、grepにファイルからパターンを読み取るように指示することによって行われます。

grep -F -f 300KFile 30MFile

これにより、大きなファイル全体で一致を順番に出力し、複数のパターンに一致する行のみを一度だけ印刷します。また、これは行のどこでもパターンを探します。たとえば、パターンファイルに含まれている場合と1234などの行が一致します。123456,345678,2348962342478912,1211138,1234

前処理パターンにより、正確な列の一致を制限できます。たとえば、パターンに特殊文字が含まれていない場合()?*+\|[]{}:

<300KFile sed -e 's/^/(^|,)/' -e 's/$/($|,)/' |
grep -E -f - 30MFile

各パターンの最初の一致のみを維持することが重要な場合は、最初のパスを作成して上記の関連行のみを抽出し、awkまたはPerlで2番目のパスを作成してどのパターンが表示されたかを追跡します。

<300KFile sed -e 's/^/(^|,)/' -e 's/$/($|,)/' |
grep -E -f - 30MFile |
perl -l -F, -ape '
    BEGIN {
        open P, "300KFile" or die;
        %patterns = map {chomp; $_=>1} <P>;
        close P;
    }
    foreach $c (@F) {
        if ($patterns{$c}) {
            print;
            delete $patterns{$c};
        }
    }
'

答え2

次のコマンドを実行できますか?

grep -Ff FILE_A FILE_B > FILE_C

これで、ファイルAとCでのみスクリプトを実行できます。

修正する:ちょっと…順序は維持されますか?

別のアップデート:注文を維持するには追加の処理が必要です。これにより、元のスクリプトと同じ結果が得られます。 FILE_Aの300,000行とFILE_Bの300,000行について、それぞれ125分と14秒間テストされました。

#! /bin/bash
grep -Ff FILE_A FILE_B > FILE_B_TMP
grep -oFf FILE_A FILE_B_TMP > FILE_A_SHUFF
grep -Ff FILE_A_SHUFF FILE_A > FILE_A_TMP

while read -r line; do
   grep -F -m1 "$line" FILE_B_TMP
done < FILE_A_TMP > result.txt

答え3

私はcommがより良いパフォーマンスをもたらすことができると信じています。

comm -12 300KFile <(sed 's/,.*//' 30MFile)

注意 300KFile の文字列が12312330M ファイルの文字列と一致する必要があるかどうかはわかりません。gdwyedg,123123,hfsjdkfhあなたのスクリプトでは一致しますが、私のスクリプトでは一致しません。

答え4

私はgrepベースのソリューションがまだFILE_AのすべてのレコードをFILE_Bのすべてのレコードと比較する必要があると思います。 FILE_AのN-1個以上のレコードがFILE_Bの特定のレコードと一致しないため、このアプローチには多くの重複があります。一方、ファイルをソートすると、各比較から多くのテストを削除できます。だから何か...

#!/bin/bash

# NB a faster solution would be to sort the smaller file in a seperate process
# you might also want to set a buffer size for large files
sort $1 > /tmp/$$.a
sort $2 > /tmp/$$.b

join -j1 -t',' /tmp/$$.a /tmp/$$.b

rm -f /tmp/$$.?

(未検証)

ただし、FILE_Bの特定のデータ列と一致させたい場合は、項目の順序が変更され、ソートのためにオーバーヘッドが発生しますが、これらのファイルサイズの場合は結果が速くなるはずです。

関連情報