grepはどのように高速に実行されますか?


113

シェルでのGREPの機能に本当に驚いています。以前はJavaでsubstringメソッドを使用していましたが、今ではGREPを使用して数秒で実行され、以前使用していたJavaコードよりも非常に高速です。 (私の経験によれば、私は間違っているかもしれません)

それがどのように起こっているのか理解できなかったと言われていますか?また、ウェブ上で利用できるものはあまりありません。

誰かがこれを手伝ってくれる?


5
これはオープンソースなので、自分で確認することができます。gnu.org/software/grep/devel.html
driis

6
ばかげた魚はあなたの質問に正確に答える素晴らしい記事を持っています:ridiculousfish.com/blog/posts/old-age-and-treachery.html
David Wolever 2012

@WilliamPursell実行時間が数秒になると、JITはおそらくウォームアップしており、心の数の違いは、(1)grepが何をするのかについて非常にスマートであること、および(2)Javaコードがかなり悪いアルゴリズムを選択していることが原因ですgrepが焦点を当てている特定の問題について。

3
Java実装がJVMの起動に費やす時間と、実際にコードの実行に費やす時間はどれくらいですか。あるいは、Javaコードで使用したアルゴリズムの問​​題かもしれません。O(N ^ 2)アルゴリズムは、どの言語でも遅くなる可能性があります。
キース・トンプソン

回答:


169

あなたの質問がGNU grep特に関係していると仮定します。これは作者のマイク・ハーテルからのメモです:

GNU grepは、すべての入力バイトを探すことを避けるため、高速です。

それはそれがあること、各バイトのために非常に少数の命令を実行しているため、GNU grepのは速いです を見て。

GNU grepは、よく知られているBoyer-Mooreアルゴリズムを使用します。このアルゴリズムは、最初にターゲット文字列の最後の文字を探し、ルックアップテーブルを使用して、一致しない文字が見つかったときに、入力をどれだけ先にスキップできるかを伝えます。

GNU grepはまた、Boyer-Mooreの内部ループを展開し、展開されたすべてのステップでループ終了テストを実行する必要がないように、Boyer-Mooreデルタテーブルエントリを設定します。この結果、制限内では、GNU grepは実際に参照する各入力バイトに対して実行されるx86命令の平均が3つ未満になります(そして、多くのバイトを完全にスキップします)。

GNU grepは生のUnix入力システムコールを使用し、データを読み取った後にデータをコピーしません。さらに、GNU grepは入力を行に分割しないようにします。改行を探すと、grepが数倍遅くなります。改行を見つけるには、すべてのバイトを調べる必要があるためです。

したがって、行指向の入力を使用する代わりに、GNU grepは生データを大きなバッファーに読み取り、Boyer-Mooreを使用してバッファーを検索します。一致が見つかった場合にのみ、境界改行を探します(次のような特定のコマンドラインオプション- nこの最適化を無効にします。)

この回答は、ここから取得した情報のサブセットです


41

スティーブの優れた答えに追加します。

それは広く知られているわけではないかもしれませんが、長いパターンでは、ボイヤー・ムーアはより長い歩幅で前にスキップしてさらに優れたサブリニア速度を実現できるため、長いパターン文字列をgrep する場合、grepはほとんど常に高速です。

例:

# after running these twice to ensure apples-to-apples comparison
# (everything is in the buffer cache) 

$ time grep -c 'tg=f_c' 20140910.log
28
0.168u 0.068s 0:00.26

$ time grep -c ' /cc/merchant.json tg=f_c' 20140910.log
28
0.100u 0.056s 0:00.17

長いフォームは35%高速です!

どうして?Boyer-Mooreは、パターン文字列からスキップ転送テーブルを構築し、不一致がある場合は常に、入力内の単一の文字をスキップテーブル内の文字と比較する前に、可能な限り長いスキップ(最後の文字から最初の文字まで)を選択します。

ここだボイヤームーア説明するビデオ (kommradHomerの功績は)

別の一般的な誤解(GNU grepの場合)は、fgrepよりも速いということですgrepfin fgrepは「高速」を意味せず、「固定」を意味します(manページを参照)。両方とも同じプログラムであり、両方ともBoyer-Mooreを使用しているため、fixed-を検索するときに速度に違いはありません正規表現の特殊文字のない文字列。私が使用する唯一の理由fgrepは、正規表現の特殊文字(、、またはなど)がある.場合に[]、そのように*解釈されないようにすることです。そしてそれでも、よりポータブル/標準の形式grep -Fが優先されfgrepます。


3
パターンが長いほど高速になります。パターンが1バイトの場合、grepはすべてのバイトをチェックする必要があります。パターンが4バイトの場合、4バイトのスキップが発生する可能性があります。パターンがテキストと同じ長さの場合、grepは1ステップしか実行しません。
noel 2014年

12
はい、それは直感的です-もしあなたがボイヤー・ムーアの仕組みを理解していれば。
arielf 2014年

2
それ以外でも直感的です。干し草の
山の

2
「長くなると速くなる」という反例は、失敗するまでに多くのテストを行わなければならず、とにかく先に進むことができない場合です。ファイルxs.txtに100000000 'x'が含まれているとするとgrep yx xs.txt、実際にはそうなりますが、実際にはそうするよりも早く一致を見つけることができませんgrep yxxxxxxxxxxxxxxxxxxx xs.txt。その場合、Boyer-Moore-HorspoolからBoyer-Mooreへの改善により、Skip-Aheadが改善されますが、一般的なケースでは、おそらく3つの機械命令だけではありません。
lrn

2
@Tinoありがとう。はい、(GNU)grep/fgrep/egrepが同じ実行可能ファイルへのすべてのハードリンクであった時代は過ぎ去ったようです。それら(およびz*grep bz*grepその場で解凍するutilsのような他の拡張機能)は、今では小さなシェルラッパーになっていgrepます。いくつかの単一の実行可能&シェルラッパーの間で、スイッチ上の興味深い歴史的なコメントは、このコミットで見つけることができます:git.savannah.gnu.org/cgit/grep.git/commit/...を
arielf
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.