1本のファイルに、続けて5つ質問をする。そのあと絞り込んで、集計して、置換して、書き出す。
その一連の仕事を丸ごと測ったら、CLIで51分17秒、GUIで15分35秒でした。実運用どおり .uwvz で締めるなら 12分29秒です。
ただし——質問が1つだけなら、ripgrepのほうが速いです。どこで逆転するのか、その分岐点まで出しました。
測ったもの
| 項目 | 値 |
|---|---|
| ファイル | us-260726.osm(OpenStreetMap アメリカ全土をXML展開・分割していない1本) |
| サイズ | 258,679,440,228 byte = 258.68 GB |
| 行数 | 4,509,830,821行(45億行) |
| マシン | Apple M4 / メモリ32GB / macOS 26.3.1 |
| 置き場所 | 外付けSSD |
| CLI | BSD grep 2.6.0(macOS標準)/GNU grep(Homebrew)/ripgrep 15.2.0/BSD sed/GNU sed 4.10 |
| GUI | 私が開発している UwView Pro v1.5.0 + Edit Upgrade |
測り方の作法(相手に最良の条件を与えるため):
- grepには
LC_ALL=Cを付ける(UTF-8ロケールより速いため) -c(件数のみ)で測る。ヒット行を端末に出すと、測っているのが検索ではなく描画になる- 各計測の前に
sudo purge(ページキャッシュ破棄)、計測間に150秒のクールダウン(M4のサーマル対策) - 時間は
/usr/bin/time -pの real
まず、物理下限を測る
$ /usr/bin/time -p wc -l us-260726.osm
4509830821
real 268.49
268.49秒=963 MB/s。「ただ1回読み切るだけ」の時間です。どのツールもこれより速くはなりません。以降の数字は、すべてこの268.49秒との距離として読んでください。
単発検索 — ripgrepは下限の+2%で走る
「New York」ほか3語で測りました。
| 語 | ヒット | BSD grep | GNU grep | ripgrep | amber | BSD grep(UTF-8) |
|---|---|---|---|---|---|---|
| New York | 100,492 | 342.19 | — | 276.77 | 355.24 | 363.54 |
| Statue of Liberty | 88 | 373.34 | — | 277.06 | 357.69 | 416.78 |
k="highway" |
60,471,216 | 474.40 | 273.66 | 275.04 | — | 503.43 |
ここで3つ、はっきりしたことがあります。
① ripgrepは定数時間で走る。 ヒットが88件でも6,047万件でも 275〜277秒、振れ幅±2秒。CPU時間は11〜16秒しか使っておらず、完全にI/O律速です。物理下限268.49秒の+2%——もうこれ以上速くなりようがないところに張り付いています。
② macOS標準のBSD grepは、GNU grepの1.73倍遅い。 474.40秒 対 273.66秒。CPU時間は383.53秒 対 66.54秒で、効率が5.8倍違います。BSD grepだけがディスクを使い切れていません。
brew install grepで入るGNU grep(ggrep)は、ripgrepとほぼ互角でした。巨大ファイルをmacOSで扱うなら、これは知っておく価値があります。
③ Rust製でも速いとは限らない。 amber(ambs)も測りました。1本のファイルを複数スレッドに分割して走査する、今回で唯一のツールです(grepもripgrepも単一ファイルには1スレッド)。結果は357.69秒——ripgrepより29%遅く、BSD grepとほぼ同じでした。並列化自体は効いています(--max-threads 1 なら421.12秒なので15%短縮)。ただしCPU時間が401秒で、ripgrepの25〜36倍。土台の効率差が並列化の利得を飲み込みました。「並列化されているか」より「1バイトあたり何命令使うか」のほうが効くという例です。ヒット数は88・100,492でgrep/ripgrepと完全一致でした。
前回、48GBのファイルでは grep 64.64秒 < ripgrep 71.49秒で、grepが勝っていました。今回は逆です。理由は説明できます——前回のストレージは0.41GB/sで、全ツールがI/O律速となり横並びになっていた。ストレージが963MB/sになると、ツールのCPU効率が露出する。「単一巨大ファイルではgrepが速い」は、遅いストレージでのみ成立する条件付きの結論でした。訂正します。
5つの質問をする
New York / Brooklyn / Central Park / Statue of Liberty / coffee_shop を順に検索します。
| ツール | 5問の合計 | 1問あたり |
|---|---|---|
| BSD grep | 1,789.22秒(29分49秒) | 357.84 |
| ripgrep | 1,389.55秒(23分10秒) | 277.91 |
| UwView Pro | 161.90秒(2分42秒) | 32.38 |
ヒット数は 100,492 / 30,871 / 14,306 / 88 / 36,506 ——CLIとGUIで1件も違いませんでした。
CLIは質問のたびに258GBを読み直します。GUIは索引を1度作れば、あとは索引を引くだけです。8.58倍。
絞り込みと集計
絞り込み4段 — New York から hospital と school を除外して Brooklyn を含むものへ。
grep 'New York' us-260726.osm | grep -v hospital | grep -v school | grep -c Brooklyn
→ 35件 / 343.52秒
UwView Proの多段階検索では 35.2秒。しかも内訳が構造の差を見せています。
1段目 New York 32.2秒 → 100,492件
2段目 含まない hospital 1秒未満 → 100,491件
3段目 含まない school 1秒未満 → 100,484件
4段目 Brooklyn 1秒未満 → 35件
2段目以降は、ストップウォッチでは測れませんでした。押した瞬間に件数が変わります。ここでの「1秒未満」は私の計測の分解能の限界であって、実際の所要はもっと短いはずです。上の35.2秒は、測れなかった3段をそれぞれ1秒として積んだ上限値です。
なぜこうなるかは構造で説明できます。2段目以降は前段の10万件だけを見ているからです。CLIのパイプは全体を1回流すしかないので343.52秒。9.76倍(これも上限側の値なので、実際の差はもう少し開きます)。
(この比較のため、GUI側の文脈は±0に設定しました。CLIのパイプは行ストリームなので、条件を揃えないと件数が合いません。揃えた結果、最終35件が完全一致しました。)
集計 — ヒット行から k="..." を切り出して頻度順に並べます。
grep 'New York' us-260726.osm | grep -oE 'k="[^"]+"' | sort | uniq -c | sort -rn | head -20
→ 344.38秒
UwView Proの「集計」では 34.0秒(10.13倍)。うち検索が32.0秒で、集計そのものと結果からの復帰はそれぞれ1秒未満・測れずでした(34.0秒はこれを1秒ずつとして積んだ上限値です)。結果はこうです。
| 値 | 件数 | 構成比 |
|---|---|---|
k="source" |
29,976 | 29.83% |
k="operator" |
17,815 | 17.73% |
k="addr:city" |
14,756 | 14.68% |
k="addr:street" |
8,458 | 8.42% |
k="name" |
8,147 | 8.11% |
上位10件までCLIと完全一致。さらに、上位20件+その他2,583を合計すると 100,492 ——New Yorkのヒット行数とぴったり同じでした。OSMのXMLは1行1タグなので、「行を数える」と「出現回数を数える」が一致したわけです。
置換して、書き出す
sed 's/New York/NYC/g' us-260726.osm > /dev/null → 741.48秒(変換のみ)
sed 's/New York/NYC/g' us-260726.osm > us-nyc.osm → 999.38秒(258GB書き出し込み)
置換は「読むだけ」の2.76倍かかります(741.48 ÷ 268.49)。CPU時間680秒で、ディスクを半分も使えていません。
GNU sed(
gsed4.10)も測りました。896.39秒——BSDのほうが速い。grepではGNUが1.73倍速かったのに、sedでは逆でした。「GNUのほうが速い」は一般則ではありません。
UwView Pro+Edit Upgrade 側:
| 工程 | 所要 |
|---|---|
全置換 New York → NYC |
62.0秒(100,992箇所) |
Save(.uwvz・28.61GB) |
138.1秒 |
| Save(プレーンテキスト) | 323.9秒 |
置換そのものは62.0秒。sedの変換741.48秒と比べて11.96倍です。原本を書き換えず、変更を差分として積むだけだからです。書き出しは最後に1回だけ行います。
全体作業の総計
保存はテキストか .uwvz のどちらか一方なので、総計も2段に分けます。
| 工程 | CLI(ripgrep基準) | UwView Pro+Edit | 差 |
|---|---|---|---|
| 開く・索引 | — | 317.8 | GUIのみ発生(1回だけ) |
| 5問の検索 | 1,389.55 | 161.9 | 8.58倍 |
| 絞り込み4段 | 343.52 | 35.2(上限) | 9.76倍 |
| 集計 | 344.38 | 34.0(上限) | 10.13倍 |
| 置換 | (保存に含む) | 62.0 | — |
| 保存(テキスト・258.68GB) | 999.38 | 323.9 | 置換込みで2.59倍 |
保存(.uwvz・28.61GB) |
相当機能なし | 138.1 | — |
| 総計(テキストで書き出す) | 3,076.83秒=51分17秒 | 934.8秒=15分35秒 | 3.29倍 |
総計(.uwvz で締める・実運用) |
3,076.83秒=51分17秒 | 749.0秒=12分29秒 | 4.11倍 |
BSD grep基準なら、それぞれ 3.72倍 と 4.64倍です。
(上限)は、ストップウォッチで測れなかった工程を1秒として積んだ値です。GUI側の総計も同じく上限値なので、実際の差はこの表より大きいことはあっても、小さいことはありません。
2段に分けた理由
上段はCLIに合わせて同じ成果物(プレーンテキスト258GB)を作った場合です。下段は、実際にUwView Proを使うときの締め方です——編集結果を28.61GBに畳んで残せて、次に開くときは索引を作り直さずに再開できるので、こちらを選びます。
成果物が違うことは書いておきます。.uwvz はプレーンテキストではありません。他のツールに渡すならテキストで書き出す必要があり、そのケースが上段の3.29倍です。逆に、この続きを自分で見るなら下段が合理的です——28.61GBで済み、次回は索引の317.8秒を払い直さずに済みます。CLIには状態を残す仕組みがないので、翌日の1問目はまた277.91秒からです。
で、何問目で逆転するのか
CLIは質問数に比例し、GUIは索引の1回きり+小さい増分です。交差点を出します。
CLI(N) = N × 277.91 (ripgrep・実測)
GUI(N) = 317.8 + N × 32.38 (索引317.8秒・1問32.38秒・実測)
N* = 317.8 ÷ (277.91 − 32.38) = 1.29
| 質問数 | ripgrep | BSD grep | UwView Pro | 倍率(ripgrep ÷ UwView Pro) |
|---|---|---|---|---|
| 1 | 277.9 | 357.8 | 350.2 | 0.79倍(ripgrepの勝ち) |
| 2 | 555.8 | 715.7 | 382.6 | 1.45倍 |
| 3 | 833.7 | 1,073.5 | 415.0 | 2.01倍 |
| 5 | 1,389.6 | 1,789.2 | 479.7 | 2.90倍 |
| 10 | 2,779.1 | 3,578.4 | 641.6 | 4.33倍 |
| 20 | 5,558.2 | 7,156.8 | 965.4 | 5.76倍 |
質問が1つだけなら、ripgrepの勝ちです(277.9秒 対 350.2秒)。索引を作る意味がありません。BSD grepとはほぼ互角。
2問目から逆転します。そこからは質問を重ねるほど開きます——3問で2.01倍、5問で2.90倍、20問で5.76倍。線引きはずっと同じで、今回それを数字で引けました。
同じ仕事をしたことの証明 — 照合6項目
速度を比べる前に、両者が本当に同じ仕事をしたのかを確かめる必要があります。6項目とも一致しました。
| # | 項目 | 値 |
|---|---|---|
| 1 | 総行数 | 4,509,830,821(wc -l = GUIの索引) |
| 2 | 5問のヒット数 | 100,492 / 30,871 / 14,306 / 88 / 36,506 |
| 3 | 絞り込み最終 | 35件 |
| 4 | 集計 上位10件と総計 | k="source" 29,976 … 総計100,492 |
| 5 | 置換箇所 | 100,992 |
| 6 | テキスト出力サイズ | 258,678,935,268 byte |
6が決定的だと思っています。GUIで非破壊編集した結果を書き出したファイルが、sed の出力と1バイトも違いませんでした。「差分ファイル方式で本当に同じ結果になるのか」という当然の疑問に、バイト単位で答えられます。
5も面白いところです。grep -c が数えた 100,492は「行数」、sed が置換したのは 100,992箇所。差の500は、1行に2箇所以上「New York」があった行です。GUI側の置換件数も100,992で一致しました。
数字にならない差
開いた瞬間から読める。 索引作成は317.8秒かかりますが、本文が見えるまではストップウォッチで測れませんでした(1秒未満)。索引を作っている最中から、末尾でも中間でも読めます。この1秒未満は索引の317.8秒の「前」ではなく「中」にあるので、総計は変わりません。grepは終わるまで無音です。
戻れる。 絞り込みの4段目から1段目のタブに戻る操作は、ストップウォッチで測れませんでした(1秒未満)。CLIならパイプを組み直してもう一度343秒です。「消しすぎたかもしれない」と思ったときのコストが、桁で違います。
中断できる。 作業途中のCloseも、開き直しての再開も、測れませんでした(どちらも1秒未満)。258GBの編集セッションを一瞬で畳んで、翌日一瞬で戻れます。原本を書き換えていないので、書き出すものが残っていないからです。
正直に書いておくこと
- すべて私の環境での実測です(Apple M4・32GB・外付けSSD)。設定の問題かもしれません——ご存じの方は教えてください。訂正します
- 私は比較対象の一方(UwView Pro)の開発者です。その分は割り引いてお読みください
- GUI側はストップウォッチによる画面操作ベースの計測を含みます。CLI側は
/usr/bin/time -pです - GUI側で「1秒未満」と書いた工程は、ストップウォッチでは測れなかったという意味です。総計・倍率は、それらを1秒として積んだ上限値で計算しています。つまり実際の差は、ここに書いた数字より大きいことはあっても、小さいことはありません
- 絞り込みと集計のCLI側はBSD grepでの実測です。GNU grepならさらに短縮される見込みですが、未計測です
- 1回きりの検索、スクリプト、cron、パイプで他へ渡す処理——ここはCLIが正解です。GUIに置き換えるものではありません
- GUIが効くのは、何を捨てればいいかがまだ分かっていない対話的な調査です。障害調査は、たいていそちらです
使ったツール
UwView Pro は販売中です(Windows・macOS・Linux対応、1ライセンスで3OS。買い切りまたは月額、14日間の無料試用あり——編集機能も使えます)。多段階検索・集計はPro機能、全置換は Edit Upgrade の機能です。
関連記事
開発者より: 私のアプリ・Kindle本・OSSの一覧は GitHub: amru195704 にあります。
ご注意
本記事の情報は参考情報として提供するものであり、正確性・完全性を保証するものではありません。すべての計測値は記載の特定環境におけるものであり、他のハードウェアでは異なる場合があります。誤りにお気づきの場合はコメントでお知らせください。確認のうえ訂正いたします。

