5 ポイント 投稿者 GN⁺ 2023-12-01 | 1件のコメント | WhatsAppで共有
  • ripgrep(rg) は、The Silver Searcher流のコード検索の使いやすさと GNU grep級の生の性能を組み合わせた Rust製コマンドライン検索ツールで、Linux・Mac・Windows向けバイナリを提供する
  • 25件のベンチマークでは、単一の大きなファイル検索と大規模ディレクトリ検索の両方において、性能・正確性の面で ripgrep を明確に上回るツールはなく、Unicode対応のコストも小さく抑えられている
  • .gitignore の処理、隠しファイル・バイナリファイルのデフォルト除外、ファイルタイプフィルタ、任意の PCRE2 サポート、複数エンコーディングや圧縮ファイルの検索、前処理フィルタまで含み、コード検索ツールの実用範囲を広げている
  • Linuxカーネルリポジトリと OpenSubtitles2016 実験の差は、リテラル最適化、Teddy SIMD 多重パターン検索、Aho-Corasick、UTF-8 デコード方式、行カウント、.gitignore 処理コストに大きく左右される
  • 多数の小さなファイルを並列検索する場合、メモリマップは遅くなることがあり、単一の大きなファイルでは有利なことがあるため、ripgrep は状況に応じて中間バッファ検索とメモリマップ検索を使い分ける

ripgrepが目指した位置

  • ripgrep は、コード検索ツールの使いやすさと grep 系ツールの性能の両立を狙ったコマンドライン検索ツールである
  • 比較対象は GNU grepgit grepThe Silver Searcher(ag)Universal Code Grep(ucg)The Platinum Searcher(pt)sift である
  • ベンチマークで確認しようとした要点は3つである
    • 単一ファイルと大規模ディレクトリ検索の両方で、ripgrep を明確に上回るツールはない
    • Unicode対応を適切に提供しつつ、大きな性能コストを要求しない
    • 複数ファイルを一度に検索する場合、メモリマップは概して高速になるというより、むしろ遅くなることがある
  • 著者は ripgrep とその基盤となる正規表現エンジンの開発者であり、ベンチマークが選別されてバイアスを含む可能性があることを明らかにしている

機能と基本動作

  • ripgrep の実行ファイル名は rg である
  • デフォルトの検索は現在のディレクトリを再帰的に探索し、.gitignore を尊重し、隠しファイルとバイナリファイルをスキップする
  • .rgignore にも対応し、.rgignore のパターンは .gitignore より優先される
  • -u-uu-uuu で ignore ファイルの無視、隠しファイルの包含、バイナリファイルの包含へと範囲を広げられる
    • rg -uuugrep -a -r に近い
  • ファイルタイプフィルタに対応する
    • rg -tpy foo: Pythonファイルのみ検索
    • rg -Tjs foo: JavaScriptファイルを除外
    • --type-add で新しいファイルタイプ規則を追加可能
  • grep の多くの機能も提供する
    • コンテキスト出力
    • 複数パターン検索
    • 色付きハイライト
    • 完全な Unicode 対応
  • デフォルトの正規表現エンジンは look-around と backreference をサポートしないが、-PPCRE2 エンジンを選択すればこれらの機能を利用できる
  • 一部の UTF-16 自動検出と、-E/--encoding に基づくエンコーディング指定にも対応する
    • UTF-16、latin-1、GBK、EUC-JP、Shift_JIS などを含む
  • -z/--search-zip により、gzip、xz、lzma、bzip2、lz4 などの圧縮ファイル検索をサポートする
  • PDFテキスト抽出、追加の展開、復号、自動エンコーディング検出などの任意の前処理フィルタにも対応する

使わない理由

  • 可搬性とどこでも使えることを最優先するなら、標準準拠で広くインストールされている grep が適している
  • 他のツールにある特定の機能やバグに依存している場合、ripgrep は適さない可能性がある
  • 一部の性能上のエッジケースでは、他のツールのほうがうまく動作する場合がある
  • インストールできない、またはプラットフォームが対応していない場合も利用できない

grep系ツールの動作構造

  • 検索ツールは大きく3段階を経る
    • 検索対象ファイルの収集
    • 実際の検索
    • 結果の出力
  • grep 系ツールは大きなファイルをうまく検索する必要があるため、正規表現エンジンの性能が重要である
  • ack 系ツールは、再帰ディレクトリ探索と .gitignore のような ignore 規則の適用を高速に処理する必要がある
  • ripgrep は2つのアプローチを組み合わせようとしている
    • 高速な正規表現エンジン
    • 並列検索
    • 検索対象のフィルタリング

ファイル収集と ignore 処理

  • ack 系ツールでは、現在のディレクトリでどのファイルを検索するかを高速に決定することが重要である
  • ディレクトリ走査の性能は、不要な stat 呼び出しの数に影響される
  • ripgrep は、システムコールを最小限に抑えることを目標にした再帰ディレクトリイテレータを使用する
  • .gitignore の処理にはコストがある
    • 各ディレクトリで ignore ファイルを探す必要がある
    • ignore パターンをコンパイルする必要がある
    • すべての候補パスにパターンを適用する必要がある
  • Linuxカーネルリポジトリには 4,640 個のディレクトリと 178 個の .gitignore ファイルがあった
  • ripgrep.gitignore の意味をより完全にサポートしようとしており、最後に定義されたマッチングパターンを優先する
  • ucg.gitignore の代わりにホワイトリストベースの glob 規則を使うため高速なことがあるが、未知の拡張子のファイルを取りこぼす可能性がある

正規表現エンジンの違い

  • 正規表現エンジンは大きく2種類に分かれる
    • バックトラッキングベース: 機能は豊富だが、一部の入力では指数時間で遅くなることがある
    • 有限オートマトンベース: 機能は制限されることがあるが、検索テキスト長に対して線形時間を保証する
  • ツールごとのエンジンは次の通りである
    • GNU grep、git grep: 独自の有限オートマトンベースのエンジン
    • ripgrep: Rust regex ライブラリ、有限オートマトンベース
    • agucg: PCRE ベースのバックトラッキング
    • ptsift: Go regex ライブラリ、有限オートマトンベース
  • agucg は PCRE の利用により、最悪時のバックトラッキング動作にさらされる可能性がある
  • 例のパターン (a*)* c は PCRE ベースのツールで問題を引き起こし得るが、他のベンチマーク対象ツールは問題なく処理する

リテラル最適化と SIMD

  • 単純な文字列検索では、正規表現エンジンより リテラル検索の最適化 のほうが重要になることがある
  • Boyer-Moore は古典的な部分文字列検索アルゴリズムであり、候補位置をすばやく見つけるために memchr のようなルーチンを活用できる
  • memchr 実装は SIMD 命令で一度に 16 バイトを検査することが多く、数 GB/s のスループットを出せる
  • Rust regex ライブラリは、パターンから prefix・suffix のリテラルを積極的に抽出する
    • foo|bar
    • (a|b)c
    • [ab]foo[yz]
    • (foo)?bar
    • (foo)*bar
    • (foo){3,6}
  • 正規表現全体が単一リテラルまたはリテラル alternation に分解できるなら、中核の正規表現エンジンをまったく使わないこともある
  • ripgrep は行単位で結果を出力する特性を活かして、inner literal も抽出する
    • 例: \w+foo\d+ では foo を先に見つけ、候補行だけを正規表現で検証する
  • 複数リテラル検索では、GNU grep は Commentz-Walter 類似アルゴリズムを使い、Rust regex は Aho-Corasick または Teddy SIMD アルゴリズムを使う
  • Teddy は Intel Hyperscan 由来の SIMD ベース多重パターン検索アルゴリズムで、ripgrep が GNU grep を上回る中核最適化の1つである

検索方式: 行単位検索を避ける

  • 素朴な実装ではファイルを1行ずつ読み、各行にパターンを適用するが、ほとんどの検索ではマッチはまれなため非効率である
  • 検索ツールは通常、大きなバイトバッファをまとめて検索する
    • ファイルをメモリマップする
    • ファイル全体をメモリに読み込む
    • 固定サイズの中間バッファで段階的に検索する
  • ripgrep、GNU grep、git grep は段階的検索をサポートし、ファイルとストリームの両方に適用できる
  • 段階的検索は実装が難しい
    • 行番号の計算
    • バッファが行の途中で終わる場合の処理
    • 長い行の処理
    • invert match の処理
    • マッチ周辺のコンテキスト出力処理
  • ripgrep は実装の複雑さを受け入れて段階的検索を採用しており、ベンチマークでは多数の小さなファイル検索でメモリマップより高速な結果を示している

出力と並列性

  • 並列検索では、各スレッドがそのまま出力すると、異なるファイルの結果が混ざる可能性がある
  • すべての並列コード検索ツールは、検索結果をメモリ上の中間バッファに書き込み、出力段階だけを直列化する
  • この方式により、検索スレッドは実際の検索を並列に実行できる
  • 欠点は、すべての行がマッチする2GBファイルのような場合、メモリ使用量が大きくなりうる点である
  • ripgrepは、stdinまたは単一ファイルの検索では、中間バッファなしでstdoutに直接書き込む

ベンチマーク方法論

  • ベンチマークは、エンドユーザーの課題を基準に次のように分けられている
    • 大規模コードリポジトリの検索
    • 単一の大きなファイルの検索
  • 検索パターンは、単純なリテラル、alternation、軽い正規表現に偏っている
  • ツールごとにデフォルト動作が異なるため、公平な比較のために行番号、Unicode、.gitignore、ホワイトリストなどの条件を揃えるようにしている
  • ベンチマーク対象のバージョンは次のとおり
    • ripgrep v0.1.2
    • GNU grep v2.25
    • git grep v2.7.4
    • ag commit cda635, PCRE 8.38
    • ucg commit 487bfb, PCRE 10.21 JIT
    • pt commit 509368
    • sift commit 2d175c
  • ackは当時、他のツールより大幅に遅かったため除外された
  • ベンチマークランナーは、Python 3.5以上を必要とするbenchsuiteで、ripgrepリポジトリに含まれている
  • 各コマンドは測定前に3回のウォームアップを実行し、OSのページキャッシュにコーパスが載るようにした
  • 各コマンドは10回測定し、平均と標準偏差を記録した
  • 実行環境は Amazon EC2 c3.2xlarge、Ubuntu 16.04、Xeon E5-2680 2.8GHz、メモリ16GB、80GB SSDである
  • 設定ログ、要約結果、raw CSVも公開されている

Linuxカーネルコード検索結果

  • コード検索ベンチマークは、ビルド済みLinuxカーネルリポジトリ commit d0acc7で実行された
  • ビルド済みカーネルリポジトリを使った理由は、ビルド成果物がリポジトリに残ることで、検索結果の関連性や性能に影響を与えうるためである
  • linux_literal_defaultでの単純リテラル PM_RESUME 検索は、各ツールのデフォルト動作の違いを示している
    • rg.gitignoreを尊重し、隠しファイル・バイナリファイルをスキップする
    • agptも似ているが、行数を数える
    • ucg.gitignoreを読まず、ホワイトリストベースで検索する
    • siftはデフォルトではほぼすべてを検索する
    • git grepは、git indexから検索対象ファイル集合を取得できる利点がある
  • .gitignoreを尊重することは、結果の関連性を高める一方で、性能面ではコストになることがある
  • linux_literalでは、rg (whitelist)ucgとほぼ同等の性能を示し、rg (ignore)git grepと近い水準だった
  • rg (ignore) (mmap)ag (ignore) (mmap)は、メモリマップの使用により遅くなり、同条件ではrg (ignore)の方がはるかに速かった
  • ローカルマシンでもメモリマップ版はより遅かったが、EC2ほど差は大きくなかった

Unicodeと大文字小文字検索

  • linux_literal_caseiでは、pt-iをGo regexpの(?i)として処理したため大幅に遅くなった
  • siftは、パターンと検索ブロックを小文字に変換する方式を使ったため、遅くなり方は比較的小さかったが、この最適化はASCIIの大文字小文字しか扱えず、Unicodeの大文字小文字処理としては不正確である
  • ripgrepは、大文字小文字を無視する検索を可能なリテラルの組み合わせに変換し、Teddyで候補位置を高速に見つける
  • linux_unicode_word\wAh検索は、Unicode対応の\wµAhのような結果を拾えるかを確認するものだ
  • Unicodeの切り替えが可能だったのはrggit grepだけで、agptsiftucgはASCII専用の\wを使っていた
  • git grepはUnicode対応を有効にすると大きな性能コストが発生したが、ripgrepでは性能低下はほとんどなかった
  • ripgrepは、UTF-8デコードを有限状態機械に組み込み、別個のデコード段階なしにUTF-8バイト列へ直接マッチさせる

正規表現の複雑さによる違い

  • [A-Z]+_RESUMEのようにリテラルsuffixを持つ正規表現では、rgucg_RESUMEを使って候補を高速に見つける
  • ERR_SYS|PME_TURN_OFF|LINK_REQ_RST|CFG_BME_EVTのようなリテラルalternationでは、ripgrepはTeddyを使い、中心となる正規表現エンジンをまったく使わないことさえある
  • 大文字小文字を無視するalternationでも、ripgrepは大文字小文字の組み合わせprefixを作ってTeddyで候補を見つけ、候補だけを完全な正規表現で検証する
  • \p{Greek}検索では、Rust regexとGo regexだけがそのUnicodeプロパティに対応しており、rgptsiftよりはるかに高速だった
  • \p{Greek}の大文字小文字無視検索では、siftはマッチを報告できず、ptはUnicodeの大文字小文字処理を正しく行えなかった
  • \w{5}\s+...のようにリテラルを含まないパターンでは、regexエンジンの性能が直接表れる
    • rgはUnicode対応時でも高速な方だった
    • git grepはUnicode対応時に大きなコストを負った
    • Unicode DFAはASCII DFAよりはるかに大きなNFA状態集合を扱い、例としてASCIIでは約250個、Unicodeでは約77,000個のNFA状態になる

単一の大きなファイルの検索

  • 単一ファイルのベンチマークには OpenSubtitles2016 のサンプルが使われた
    • 英語サンプルは約1GB
    • ロシア語サンプルは約1.6GB
  • この領域では、正規表現エンジンの性能とリテラル最適化がより重要になる
  • subtitles_literalでは、Sherlock HolmesШерлок Холмсの検索はいずれもrgが最速だった
  • ripgrepは、リテラルについて出現頻度の低いバイトを選び、memchrで使おうとする
    • 標準的な Boyer-Moore 実装は通常、候補検索に最後のバイトを使う
    • rgは、よりまれなバイトを選ぶことで、SIMD最適化ループでより長くスキップしようとする
  • ロシア語パターンはUTF-8では多くの文字が\xD0または\xD1で始まるため、先頭バイト検索が非効率になる可能性がある
  • rgは、事前計算された256バイト頻度表を使い、\xD0\xD1よりも出現頻度の低いバイトを優先する
  • 単一の大きなファイルでは、メモリマップを一度作ればよいため、rgのメモリマップ検索はrg (no mmap)より約25%速かった

単一ファイルでのUnicodeとalternation

  • subtitles_literal_caseiでは、rgはUnicodeの大文字小文字無視検索を正しく処理しながら高速だった
  • GNU grepはUnicodeの大文字小文字無視検索で大きなコストを負った
  • ロシア語の大文字小文字無視検索では、grep (ASCII)-iを事実上無視しているように見え、agは0件のマッチを報告した
  • subtitles_alternateでは、複数の人物名を含むalternation検索で、rgが英語・ロシア語の両方で最速だった
  • 英語のalternationでは、rgはGNU grepより約1桁速かった
  • subtitles_alternate_caseiでは、rgは前よりかなり遅くなったが、英語ではなお他のツールを上回った
  • この場合は、Teddyが処理するにはリテラル候補が多すぎるため、rgはTeddyの代わりにAho-Corasickへ切り替える
  • ripgrepは、transition tableベースの「advanced」Aho-Corasickを使い、入力バイトごとに1回の遷移を実行する

inner literal とリテラルのないパターン

  • \w+\s+Holmes\s+\w+ のようなパターンは prefix・suffix リテラル最適化を避けるように構成されているが、内部リテラル Holmes を活用できる
  • ripgrep と GNU grep は inner literal 最適化を行う
  • ripgrep は Rust regex の regex-syntax を活用し、パターン AST からリテラルを抽出する
  • ロシア語版 \w+\s+Холмс\s+\w+ では、Unicode を正しくサポートするツールだけが意味のある結果を出せた
  • リテラルがまったくない長い \w{5}\s+... パターンでは、rg は英語で最速クラスであり、GNU grep の Unicode 対応版は英語で 90 秒以上、ロシア語で 4 分以上かかったため除外された
  • ripgrep は UTF-8 デコードを DFA に組み込む方式で、Unicode サポートを維持しつつ性能を確保している

追加ベンチマーク

  • everything は Linux リポジトリで .* によってすべての行をマッチさせる非現実的なテスト
    • rg は 22,065,361 行を 1.081 秒で報告した
    • agpt はすべての行を報告せず、マッチ数の制限があるように見える
  • nothing.* に invert match を適用し、どの行も報告しないテスト
    • rg は 0.302 秒、git grep は 0.905 秒を記録した
    • ptucg は invert search をサポートしていない
  • context は英語字幕コーパスで Sherlock Holmes の前後 2 行の文脈を出力する
    • rg は 0.612 秒、sift は 0.717 秒でほぼ同等だった
    • ucg はこの機能をサポートしていない
  • huge は 9.3GB の英語字幕全体で Sherlock Holmes を検索する
    • rg は 1.786 秒、GNU grep は 5.119 秒、sift は 3.047 秒を記録した
    • ucg は行数カウント条件で 1,543 行しか報告せず誤った結果を出しており、2GB 超のファイル検索で問題が発生したと疑われる

結論

  • ripgrep は Linux カーネルリポジトリの検索で、すべてのベンチマークに常に勝ったわけではないが、性能と正確性の両面で他のツールが明確に優位だとは言いにくかった
  • git grep は一部の単純なケースで数ミリ秒先行することがあったが、パターンが複雑になったり Unicode が必要になったりすると、ripgrep が大きく上回る場合があった
  • ripgrep のコード検索性能には、次の要素が寄与している
    • stat 呼び出しの最小化を目標にした高速なディレクトリ走査
    • RegexSet を用いた .gitignore glob マッチング
    • Chase-Lev work stealing queue による作業分配
    • 多数の小さなファイルの検索でメモリマップを使わないという選択
    • 高速な正規表現エンジン
  • 単一ファイル検索では、ripgrep はすべての主要ベンチマークで最速か、大差をつけて先行した
  • 単一ファイル性能には、疎バイトベースの memchr、Teddy SIMD、Aho-Corasick、UTF-8 デコード内蔵 DFA が影響している
  • Unicode 機能を必要とするベンチマークでは、rg、GNU grep、git grep だけが意味のあるサポートを示し、GNU grep と git grep は概して大きな性能コストを伴った
  • メモリマップは Linux x86_64 基準で、多数の小さなファイルの並列検索では不利であり、単一の大きなファイルの検索では有利で、VM 環境では追加のペナルティが発生する可能性があった

1件のコメント

 
GN⁺ 2023-12-01
Hacker News の意見
  • 確かに速く、fzf との組み合わせをいつも勧めたくなる
    まず ripgrep で探し、その結果のファイル+テキストに対してファジー検索を重ね、bat で文脈を表示する PowerShell 関数として使っている
    複数のリポジトリが混在するプロジェクトで、「どこかにあるのは分かっているが、正確な場所や名前が分からない」ときに非常に素早く絞り込める
    この方法は https://github.com/junegunn/fzf/blob/master/ADVANCED.md に載っているもので、全部を使わないとしても、アイデアを得るために目を通す価値がある

    • さらに一歩進めて、ripgrep-all(rga)fzf と統合することを勧める
      テキストファイルだけでなく、PDF や zip などさまざまなファイル形式までファジー検索できる
      詳細は https://github.com/phiresky/ripgrep-all/wiki/fzf-Integration にある
    • これを bash 版としても書いた
      rg の結果を fzf で選び、選択したファイルと行番号をパースして $EDITOR +"${linenumber}" "$file" で開く方式だ
    • Vim で fzf+rg がないと、ほとんど壊れたような感覚になる
      電動グラインダーではなく手でコーヒー豆を挽いているようなものだ
    • fzf を使うと、Git に追加するファイルを大量に選びつつ、一部はスキップできる
      gitconfig[alias]fza = "!git ls-files -m -o --exclude-standard | fzf -m --print0 | xargs -0 git add" を入れると、git fza で変更済みまたは未追加のファイル一覧が表示され、スペースで項目をトグルしながら次へ移動できる
      このエイリアスと fzf+fd が、ワークフローの一部をかなり速くしてくれる
      macOS で zsh 設定に入れるものをまとめたガイドもある: https://gist.github.com/aclarknexient/0ffcb98aa262c585c49d4b...
    • 私も ripgrep をほぼ同じように使っている
      数百のリポジトリがあるコードベースで、ファイルやプロジェクトを絞り込む出発点として使い、その後さらに掘り下げる
  • Emacs で ripgrepproject.eldumb-jump パッケージで使っている
    最も人気のある方法ではないかもしれないが、全体としての体験はかなり満足している
    package-installdumb-jump をインストールし、(add-hook 'xref-backend-functions #'dumb-jump-xref-activate) だけ設定すればよい
    Python プロジェクトで M-. または C-u M-. で識別子の定義を探すと、dumb-jump が現在のプロジェクトとファイル形式に合わせて rg コマンドを実行し、結果を Xref バッファに表示する
    ag もサポートしており、agrg がなければ grep に戻るが、ホームディレクトリ全体を探すときは予想どおり遅くなることがある

    • Emacs 標準搭載の project.el だけでも ripgrep はかなり簡単に使える
      外部パッケージが必ず必要というわけではなく、大きなディレクトリで遅い grep の代わりに使うには (setq xref-search-program 'ripgrep) を設定すればよい
      すると C-x p g foo RET のようなプロジェクト検索が、現在のプロジェクトで rg -i --null -nH --no-heading --no-messages -g '!*/' -e foo の形で実行される
      結果は Xref バッファに出るので、npRETC-o などのキーで次/前のマッチへの移動、ソースへのジャンプ、分割ウィンドウ表示がしやすい
    • ripgrep の作者として見ると、その正規表現は実際に実行してはいないが、--pcre2 フラグは外してよさそうだ
      2つ目と3つ目の \b アサーションも外してよさそうで、1つ目は必要かもしれない
    • Deadgrepripgrep を使い、evil-collection のバインディングもあるので快適に使える: https://github.com/Wilfred/deadgrep
    • この方法も良いが、複数のプロジェクトを一度に探したい場合や、プロジェクト内のサブフォルダだけを探したい場合は、今でも rg.el を使っている
      そういうときは、以前なら rgrep を使っていた場面だ
  • 興味深いのは、VS Code の検索も今では Node.js ラッパーを通じて ripgrep で動作していることだ
    https://www.npmjs.com/package/@vscode/ripgrep

    • VS Code は依頼したりインストールしたりできるが、ripgrep は入れられない環境なら、これは非常に良い
      VS のインストールパス内で rg バイナリを見つけられる。少なくとも私の Windows の職場環境では可能だった
    • VS Code は Electron アプリなのに、検索がなぜあれほど速いのかずっと不思議だったが、今では理由が分かった
    • 新機能ではなく、VS Code には7年前から入っていた
  • ripgrep を2年ほど使ってきて、今ではなくてはならないツールになった
    grep から乗り換えた主な理由は使いやすさだった
    デフォルトで .gitignore のルールを尊重し、隠しファイル/ディレクトリやバイナリファイルを飛ばしてくれるため、rg search_term directory は対応する grep コマンドよりずっと良く、速度向上はおまけだ
    マッチが長すぎてターミナルがめちゃくちゃになるときは、-M 1000 のように -M オプションをよく使う

    • -M は本当に素晴らしい
      見たくない minified ファイルの結果を無視するときに特に便利で、-g *.cs のように -g オプションで特定の拡張子のファイルだけを検索するのも良い
      単独で実行できるポータブルなバイナリである点も有用で、新しいマシンで作業するときに実行ファイルを置き、grep のエイリアスを rg にしておけば、慣れたように grep と打っても rg が実行される
  • 2023年でもなお当てはまる話かもしれないが、問題は並列化された grep 代替ツール、たとえば ripgrepag が従来の grep より速すぎて、相互のわずかな速度差が判断基準になりにくいということ
    90万行のコードベースで Emacs 内から ag を使っているが、16コアの Ryzen Threadripper 2950X では実質的に即座に終わる
    1秒未満を「もう少し短い1秒未満」に縮める必要は感じない
    新しい grep 系ツールの重要な特性は速度ではなく、別の方法で評価・比較すべき

    • 2016年には速度が間違いなく重要な特性だったと思う
      ag にはかなり大きな性能の崖があり、ブログ記事でも確認できる
      ただしワークロードは人それぞれなので、場合によっては性能差が重要でないこともある
      90万行はそれほど大きい部類ではなく、単純なクエリなら、素朴すぎない grep 系ツールのほとんどは非常に速く処理する
      別の比較基準で見ると、ag はほぼ延命状態で、Debian から削除されかけたところを誰かが救ったように見える: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=999962
      ブログ記事ではUnicode対応も比較しているが、ag には実質的に Unicode 対応がない。全員に重要とは限らないが、非性能面の比較基準としては十分
    • 自分の経験では、こうしたツールはどれも強く入出力ボトルネックに縛られている
      検索時間はファイルがディスクから読み込まれる時間だけかかり、その後の差は意味を持ちにくい
      ファイルがキャッシュにある場合は、検索時間よりファイルシステムを移動してコマンドを入力する時間のほうが支配的なので、やはり性能差は意味を持ちにくい
  • タイトルには (2016) が必要
    これは元の発表記事であって、新情報ではない

  • qgrep よりは速くない
    両者の動作方式は大きく異なり、qgrepre2 ベースだが、速度が出るのはインデックスがあるため
    大きなファイルリポジトリでは、毎回すべてのファイルを走査するより qgrep とインデックスを使うほうが理にかなっているのに、なぜ人々が qgrep という選択肢を忘れるのか不思議
    ただし UTF-8 で複数行マッチングが必要なら、ripgrep は別の PCRE2 ライブラリにフォールバックする必要があるので、それほど速くないと思う

    • ripgrep の作者として言うと、qgrep がインデックスを使うため、インデックスを使わないツールより有利なのは確か
      その代わりインデックスを設定して維持する必要があるため、UX は「ただ検索を実行する」ほど単純ではない
      人々が qgrep を使わない理由は、「自分には grep でも十分速い」という理由で ripgrep を使わないのと似ている
      小さな検索対象では、ripgrepgrep、あるいは qgrepripgrep の速度差を体感できないことが多い
      Linux カーネルの検索を ripgrep が 100ms 未満で終えるなら、標準的な対話的利用でインデックス付きツールに乗り換えるほど不便かどうかは状況次第だが、たいていはそうではないはず
      ripgrep にインデックス機能を追加するアイデアを考えたことはある: https://github.com/BurntSushi/ripgrep/issues/1497
      そして複数行検索はPCRE2を必要としない。標準の正規表現エンジンにも Unicode 対応があり、PCRE2 なしでビルドしても複数行検索のサポートは維持される
  • ripgrep から ugrep に乗り換えてから、振り返らなくなった
    速度も同程度だが、ファジーマッチングがあり、コードレビューに使える TUI もあり、PDF や圧縮ファイルの中も検索できる
    オプションで Google 検索構文を使えるのも便利
    https://ugrep.com

    • 私は ripgrep の熱烈なファンだが、最近 ripgrep にはない機能であるzip 圧縮内部の検索が必要になり、ugrep を見つけた
      ディスクに展開せずに検索できる
      数百万個の小さなテキストファイルからなる圧縮コーパスを扱っているが、全体をファイルシステムに展開する必要がなくなって助かっている。ファイルシステムによってはこの規模が厳しい
      どちらのツールにも感謝しているし、それぞれの作者にも感謝している
    • grep で Google 検索構文を使い始めたら、結果の大半が何かを売ろうとしてきそうで怖い
    • 「ugrep vs ripgrep」の記事を軽く探していたら、ugrepripgrep の作者たちが Reddit で何年も言い争っていたらしい投稿を見た
      たとえば https://www.reddit.com/r/programming/comments/120wqvr/ripgre...
      ただのオープンソースツールの話なのに、少し奇妙に感じる
    • TUI が結果を fzf に渡すより良いのか気になる
      私には fzf設定可能性と柔軟性に勝つのは難しそうに見える
    • これを教えてくれてありがとう
      キラー機能は既存の grep コマンドラインオプション互換性のように思える
      まったく新しいオプション群を覚える必要がないのはかなり良い
  • なぜ grep は置き換えられたり改善されたりしないのか気になる
    この話題も、もう少し古くなってきた気がする

    • 説明できる理由はたくさんある
      慣性、互換性、変化への抵抗、イノベーターのジレンマのようなもの。否定的に言っているわけではなく、自分にもすべて当てはまる
      互換性については FAQ を見ればよい: https://github.com/BurntSushi/ripgrep/blob/master/FAQ.md#pos...
    • 今座っている40年物の椅子を Razer UltraSeat XR3000-A に替えない理由と似ている
      快適で、周囲の作業環境によく合っていて、わざわざ替えて全部合わせ直す理由がない
      Razer のような椅子が近くにすでにあって、服を預けているという点までなら、この比喩は成り立つ
    • Unix を設計した誰かが、一部のシステム機能を中核的な OS 機能であると同時に人が使う道具として作り、その結果、数十年後には「xyz というプログラムが必ず存在し、この引数を受け取り、正確にこのように動作しなければならない」といった奇妙な結果が生まれた
    • すでに ripgrep のような複数の代替ツールを使える
      grep コマンド自体を別のユーティリティに置き換えようという話なら、得られる価値に比べて壊れるものが多すぎるように見える
      より速い grep が欲しい人は別のツールを使い、従来の grep を使う人は使い続ければよいので、すでに理想的な状態に近い
    • grep はあらゆる種類のファイルからテキストを探す汎用ツールであり、UNIX 標準に組み込まれている
      一部のプログラマーはソースコード検索に使うが、他の人はソースコードとは無関係なテキスト検索やスクリプトで使っており、絶対にクラッシュしないことを期待している
      一方 ripgrep は主にソースコードリポジトリ検索のために設計された、専門的で主張の強いツールである
      汎用テキスト検索をさらに速くする余地はあまりない。mmap() を使えば切り詰められたファイルでクラッシュするリスクがあり、正規表現の表現力を落とせば速くできるかもしれず、すべての locale と文字セット対応を捨てて UTF-8/UTF-16 だけをハードコードすることもできるが、そうすべきではない
  • Portage で探してみると、PDF や doc など他の文書まで扱うバージョンもあるようだ
    https://github.com/phiresky/ripgrep-all