束ねる1回目は ripgrep に1.58倍負ける — Wide Field④:2回目からは9.77秒、ファイルが1本消えても作り直さない

技術解説

1本、消えました。

まとめて探すために作った索引の、元のファイルです。app.log.9.gz が保存期間を過ぎて消えた——そのとき、索引はどうあるべきでしょうか。

以前のこちらは、作り直していました。それが正しくないと気づくまでの話です。

先に結論: 束ねる1回目は ripgrep に 1/1.58 の負け。2回目からは 9.77秒(ripgrep 22.16秒の 2.27倍)。そして元ファイルが1本消えても、全部消えても、索引を作り直さずそのまま探せます

ripgrep は状態を持ちません。同じログ群を何度探しても、毎回ゼロからファイルを読みます。こちらは1回目に束ねた .uwvz(uvp 用の圧縮形式+索引)を作り、2回目からはそれを読みます。gz 7 本で、1回目は 35.70秒 対 ripgrep 22.54秒=1/1.58 の負け、2回目は 9.77秒 対 22.16秒=2.27倍。さらに v1.7.2 から、元ファイルが減った・見つからない・読めないときは作り直さず、前の索引で探します(Keep)。全部消えても探せます。過去ログを消したあとでも、束ねた索引の中身は残っているからです。Mac(Apple M4・10コア)・メモリ32GB・外付け USB SSD・OpenStreetMap の XML。

uvf・uvp は私が作っている UwView(v1.7.3.6 “Wide Field”・2026年9月28日公開)の検索コマンドです。uvf は無料版、uvp は有料版 UwView Pro のものです。速さの数字は v1.7.3.6 を 2026年9月29日に測ったもの、作り直しの規則は v1.7.2 で入れたものです。


先に負けを書きます

  • 束ねる1回目は、はっきり負けます。 gz 7 本で 1/1.58。索引を書くぶんです。1回しか探さないなら、無料の uvf か ripgrep のほうが速い。
  • ディスクを食います。 平文5本+gz7本=12 本を束ねた索引は約 8.2GB でした(uvp 1.7.3.5 での計測例)。元ファイルを残したままなら、その分が上乗せになります。
  • 「増えた分だけ足す」経路の速さは測っていません。 仕様としては実装済みですが、追記が何秒で終わるかの計測が無いので、この記事では速さを書きません。

ripgrep は、毎回ゼロから読む

まず相手の設計を確認します。ripgrep は検索のたびにファイルを開いて読みます。前回の結果も、前回読んだ内容も、何も持ち越しません。

これは弱点ではなく、選んだ設計です。状態を持たなければ、古い状態と実体がずれる事故が起きません。ファイルが消えていようが増えていようが、そのときの実体がそのまま答えになります。インストールしてすぐ使えるのも、この設計のおかげです。

代わりに、同じログ群を10回探せば10回読みます。回数が増えるほど、同じ仕事を繰り返します。

こちらは逆を選びました。1回目に束ねた索引を作り、2回目からはそれを読む。そのかわり、「実体と索引がずれたときどうするか」という問題を自分で引き受けることになります。

束ねた索引は、名前で突き合わせる

uvp 'osm17/*.gz,osm17/*.osm' 東京 のように渡すと、1回目に対象を展開して1本の .uwvz にまとめ、そのまま探します。2回目に同じコマンドを打つと、索引が使えるかどうかを確かめてから読みます。

確かめ方が、最初は位置でした。1本目・2本目・3本目……と並び順で照合していたのです。

これが事故のもとでした。途中の1本が消えると、それ以降が全部「並びが変わった」に見えます。結果、まるごと作り直しに倒れる。logrotate を使っていれば、古いファイルが消えるのは日常です。

v1.7.2 で、名前で突き合わせるように直しました。1本消えても、残りは同じ名前のまま同じ部品として見つかります。

「消えたら作り直す」をやめた

名前で突き合わせられるようになったので、消えたときの振る舞いそのものを決め直しました。

状態 以前(v1.7.1.x) いま(v1.7.2 以降)
元ファイルが減った・見つからない・読めない 作り直し(消えた中身を失う) 作り直さず、前の索引で探す。警告を出して --rebuild を案内
対象が0本(全部消えた) 「一致なし」でエラー 前の索引で探せる
増えたものと減ったものが同時 作り直し 作り直さない(足しもしない)
中身が変わった・並びが変わった(全部そろっている) 作り直し(予告あり) 同じ
--rebuild — 古い索引を .bak-日時 に退避してから作り直す

zip の中のエントリが減ったときも同じ扱いです。

古い索引で探して、正しいのか

ここがいちばん疑われるところだと思うので、見出しを立てます。

正しくない場合があります。だから警告を出します。

索引は、束ねた時点の中身を持っています。元ファイルが消えたあとにその索引を探すと、消えたファイルの中身が結果に出ます。実体にはもう無い行です。

これを「間違い」と呼ぶか「保管」と呼ぶかは、使い方で変わります。保存期間を過ぎて消えた古いログを、あとから追いたいことはあります。作り直してしまうと、その中身は永久に失われます。

だから既定は「作り直さない」にしました。そのうえで、ずれていることを黙らない。消えたファイルの名前を出して警告し、実体に合わせたいなら --rebuild を、と案内します。--rebuild は古い索引を .bak-日時 に退避してから作り直すので、案内に従っても過去の中身は消えません。

壊れるかもしれない操作を、黙って既定にしない。 決めたのはそれだけです。

増えた分は、名前の並びの位置に足す

逆に、対象が増えたときは索引に足します。名前の並びで途中に入るファイルでも、索引の末尾に追加する形にしました(順序は探すときに解決します)。.ignore などで対象から外れた部品は、索引には残したまま探さない扱いにします。

繰り返しになりますが、この追記が何秒で終わるかは測っていませんので、速さの話としては書きません。書けるのは「全部作り直しではない経路がある」という仕様までです。

logrotate の下で、どう動くか

実際の運用に当てはめます。app.log が毎日ローテートされ、古いものから .gz になり、保存期間を過ぎたものが消えていく——よくある形です。

  • 今日の分が増えた … 索引に足す
  • 一番古い .gz が消えた … 作り直さない。索引の中の古い行は、まだ探せる
  • 増えたものと消えたものが同時に起きた … 作り直さない(足しもしない)
  • 実体に合わせたい … --rebuild を明示的に打つ。古い索引は .bak-日時 に残る

つまり、索引は「実体の写し」ではなく「探すための保管庫」として振る舞います。ripgrep の「実体がそのまま答え」とは、性格が違います。どちらが正しいというより、何を答えにしたいかの違いです。

数字

gz 7 本(osm17 の OpenStreetMap XML・当たり 122,441 件)を1コマンドで探したときの値です。

ripgrep uvp
1回目(cold・索引を作りながら) 22.54秒 35.70秒=1/1.58
2回目(hot・索引を読む) 22.16秒 9.77秒=2.27倍
  • 1回目に余分に払うのが約 13秒、2回目からは1回につき約 12秒ずつ縮むので、2回目でほぼ取り返します。
  • 件数は ripgrep と一致しています。同じ日の統合テストでは、複数ファイルの判定 36 項目が不一致 0 件でした。
  • 作り直しの規則そのものは、「1本消す」「全部消す」を含む複数ファイルの試験で 27 項目すべて合格しています(v1.7.2 の確認時)。

測り方

  • ファイル: osm17 の OpenStreetMap(日本)XML・gz 7 本
  • 語: 東京(固定文字列・行番号つき)
  • 状態: sudo purge 後の 1回目(cold) と、続けて走らせた 2回目(hot) を分けて記録
  • 相手: ripgrep 15.2.0(rg -z -n -F)
  • こちら: uvf/uvp v1.7.3.6 “Wide Field”(2026年9月29日の統合テスト)。索引の大きさの例は uvp 1.7.3.5・12 本の別計測
  • Mac(Apple M4・10コア・メモリ32GB)/外付け USB SSD。秒数は環境によって変わります

持ち帰れる一般論

(1)状態を持つと決めたら、「ずれたとき」の規則まで含めて設計になる。 索引を持つ利点(2回目 2.27倍)と、実体とずれる問題は同じ選択の表と裏です。速さだけ取って、ずれの扱いを後回しにすると、以前のこちらのように「消えたら作り直す」といういちばん損な既定に落ち着きます。

(2)突き合わせの鍵に「位置」を使わない。 並び順で照合すると、1つ抜けただけで以降が全部ずれます。名前のような、要素そのものに属する鍵で照合すれば、抜けは抜けとして局所に収まります。これは索引に限った話ではありません。

(3)取り返しのつかない操作を、既定にしない。 作り直しは「消えた中身を失う」操作でした。失われる側に倒れる既定は、たとえ整合性のためでも既定にしない。ユーザーが明示的に頼んだとき(--rebuild)だけ実行し、それでも退避を残す。

(4)測っていない機能の速さは書かない。 追記の経路は実装済みですが、秒数が無いので速さとして書きませんでした。仕様と計測は別物で、混ぜると記事全体の数字が疑われます。

正直に書いておくこと

  • 数字はすべて Mac(Apple M4・10コア)・メモリ32GB・外付け USB SSD・OpenStreetMap の XMLでの測定例です。環境が変われば変わります
  • 索引の大きさ(約 8.2GB)は、平文5本+gz7本=12 本を束ねた別の計測例です。この記事の速さは gz 7 本のもので、同じ組の索引の大きさは記録していません
  • 追記(増えた分だけ足す)の所要時間は未計測です。仕様としてのみ書いています
  • 元ファイルが消えたあとに古い索引で探すと、実体に無い行が出ます。 警告は出しますが、実体と一致した結果が要るなら --rebuild が必要です
  • 1.5倍未満は「差」と呼んでいません。この記事で「差」と書いたのは 1/1.58 と 2.27倍だけです
  • Windows と Linux の結果は持ち込んでいません。 同じ日の統合テストで3 OS とも不一致 0 件でしたが、機械も媒体も違うので秒数は OS をまたいで比べません
  • 私は比較の一方の開発者です。その分は割り引いてお読みください

使っている道具

UwView は、巨大なテキストファイル・巨大ログを調べるための CLI/GUI ツールです。v1.7.3.6 “Wide Field” では、複数のファイルと 7 つの圧縮形式を、外部のコマンドなしで1回で探せます。

uvp ならこう書く

# 1回目:束ねた .uwvz を作りながら探す(UwView Pro)
uvp 'app.log,app.log.*.gz' ERROR

# 2回目以降:同じコマンドで、索引を使う(UwView Pro)
uvp 'app.log,app.log.*.gz' ERROR

# 実体に合わせて作り直す(古い索引は .bak-日時 に退避される)
uvp 'app.log,app.log.*.gz' ERROR --rebuild

# 対象に何が入るかだけ先に見る(無料)
uvf 'app.log,app.log.*.gz' --files

# 索引を持たずに、その場で探す(無料)
uvf 'app.log,app.log.*.gz' ERROR

# 束ねた .uwvz から取り出す(平文・gz・zip は元の形、他は平文になる)
uvp app.uwvz -extract -out ./restored/

終了コードは grep と同じで、0=あり・1=なし・2=エラーまたは結果が不完全です。

無料版は GitHub Releases から。Mac は brew install --cask amru195704/uwview/uwview、Windows は Scoop(scoop bucket add uwview https://github.com/amru195704/scoop-uwview → scoop install uwview)でも入ります。

リンク

  • 圧縮ログの検索を ugrep・ripgrep・zgrep と比べた(7 形式・他の道具も含めた一覧): https://uvp.y42u.net/blog/uwview-ugrep-ripgrep-zgrep-compressed-logs/
  • .uwvz を作って、中を探して、取り出す: https://uvp.y42u.net/blog/uwvz-compressed-archive-search-extract/
  • ログは圧縮したほうが、速く探せます(形式の選び方): https://uvp.y42u.net/blog/log-compression-format-choice/
  • gz を展開せずに探す(1本の gz の話): https://uvp.y42u.net/blog/uwview-fl08-gz-without-extract/
  • 読む回数だけを減らした13回のまとめ: https://uvp.y42u.net/blog/uwview-fl13-first-light-recap/
  • 実測の全データ(ベンチマーク一覧): https://uvp.y42u.net/benchmarks/
  • ソースコード(GitHub): https://github.com/amru195704/UwView

そして、ストレージを専有している巨大ログを圧縮して保管し、さらに高速に検索したいなら UwView Pro をどうぞ。永続索引・圧縮キャッシュ検索・約 1/9 保管で、開き直しも検索も一段速くなります(全 OS 対応・買い切り/月額プランあり)。


開発者より: アプリ・Kindle 本・公開プロジェクトの一覧は GitHub: amru195704 にまとめています。


タイトルとURLをコピーしました