巨大ファイル実測の総集編 — 数字が逆転する4つの境界線

技術解説

上限は950MB/sでした。

その「○秒」という数字、どのサイズで、何回目に測ったものですか。

同じツール・同じファイルでも、この2つを変えると勝敗はひっくり返ります。連載で出してきた実測を並べ直すと、向きが変わる境界線がはっきり4本見えました。

今回は連載の締めとして、その4本——「対応容量」という表記が意味しないこと、索引が終わるまで先頭しか見えない設計、秒数より「何回読むか」、そしてログではない巨大テキスト——を、数字の読み方として整理します。

先に結論: UwView は 258.68GB・45億行の1ファイルを、索引作成中から末尾まで読めます(索引の完了まで5分28秒=約789MB/s)。Pro は索引と圧縮を保存するので、2回目以降は行番号付きで瞬時に開き直せます(47.73GBのテキストで実測0.02〜0.07秒)。ただし「開くだけ」の速度では klogg に負けています(51GBで52.55秒 対 100.6秒)。いずれも特定環境での測定例で、環境により異なります。詳細は記事末尾

本記事の数字はすべて、測定に使った1台・1本のファイルでの結果です。ディスクの種類・接続方式・ファイルシステム・ページキャッシュの状態で大きく変わるため、そのまま一般の性能比較として読まないでください。


1. 「対応容量」は、調査できる容量ではない

状況

48GBのファイルを渡されました。使っているエディタの仕様表には「ファイルサイズ無制限」と書いてあります。安心して開きます。

開きました。先頭が表示されます。

末尾へ飛ぼうとしたら、行けません。 見えているのは18,951行まで。ファイル全体は約8.92億行あります。

なぜ起きるか

「開ける」という言葉が、製品ごとに違う意味で使われているからです。

仕様表の「対応最大サイズ」は、たいてい「クラッシュせずに読み込みを開始できるか」を指しています。「読み込みが終わるまでどこまで操作できるか」は別の話で、そこは書かれていません。

約48GB・約8.92億行のOpenStreetMap日本データを Windows ノートPC(Core i7-1165G7・RAM 16GB・内蔵SSD)で開いたときの実測です。全体を読み込んでから操作させる作りのエディタでは、読み込み進捗がこう進みました。

  • 1分14秒:29%
  • 2分35秒:50%
  • 4分06秒:100% ← ここでようやくファイル全域へ移動できる
  • 索引作成の完了までは246秒

これは「普通のエディタの動作例」として読んでください。 巨大ファイル専用のモードを持つ製品もあり、そちらを有効にしたときの挙動は測っていないので、特定の製品名は出しません。 ここで言いたいのは製品の優劣ではなく、指標の話です。

つまり、仕様表の「無制限」と、作業を始められるまでの4分06秒は、まったく別の指標です。 そして後者のほうが、調査の体感を決めます(第1回)。

汎用ツールでの対処と限界

待たずに済ませたいなら、全体を開かないのが定石です。

# サイズと行数の見当をつける(行数の確定には全走査が要る)
ls -l osm-japan.osm
wc -l osm-japan.osm    # 48GB では数分かかる

# 末尾だけ見る
tail -n 200 osm-japan.osm

# 必要な範囲だけ切り出す
split -b 2G osm-japan.osm part-

これで「見る」までは到達できます。

限界は3つ。

ひとつめ、split は原本を増やすこと。48GBを2GB刻みにすれば、24本と48GBの空き容量が必要です。そして分割点は内容を無視するので、XMLのタグやレコードの途中で切れます。

ふたつめ、tail で末尾は見えても、検索できないこと。末尾200行の外に答えがあれば、また全体に戻ります。

みっつめ、仕様表からは判断できないこと。「対応容量」「最大行数」の欄を比べても、4分06秒は出てきません。自分のファイルで測るしかないのが現状です。


2. 索引が終わるまで、先頭しか見えない

状況

ログビューアで50GBのログを開きます。プログレスバーが出ます。スクロールバーのつまみが、じわじわ小さくなっていきます。

見たいのは末尾です。障害は直前に起きているからです。

終わるまで、末尾に何が書いてあるかは分かりません。

なぜ起きるか

N行目へ跳ぶには、N行目が何バイト目から始まるかの表(索引)が要るからです。

テキストファイルに行の区切りはありません。改行を数えるまで「1億行目」の位置は分かりません。だから設計は3択になります。

  • 全部作ってから見せる — 行番号は正確。ただし完成まで待つ
  • 作らずに逐次読む — すぐ見える。ただし行番号が出ず、位置も分からない(less がこれに近い)
  • 裏で作りながら、全域を見せる — 待たない。ただし実装が難しい

多くのビューアが1番目を選ぶので、「索引が終わるまで先頭しか見えない」が起きます。

ここで大事なのは、待ち時間の大半はツールの怠慢ではないことです。klogg で 51.25GB のファイルを開くと 52.55秒。読み出し速度に直すと 930MB/s です。同じディスクの素読みが約 950MB/s なので、ほぼ出し切っています。

速度では、もうこれ以上縮みません。縮められるのは「待つかどうか」=設計のほうだけです。

汎用ツールでの対処と限界

末尾を先に見るなら、こうなります。

# 末尾から遡って読む
tac huge.log | head -n 500

# less は末尾へ行くとき全体を読む(G は待たされる)
less +G huge.log

# 行数を数えずに、末尾のバイトだけ取る
tail -c 5000000 huge.log | less

末尾は見えます。

限界は3つ。

ひとつめ、行番号が原本と一致しないこと。tail -c で切った先頭行は途中から始まっているので、そこを1行目と数えると全部ずれます。報告書には書けません(第24回)。

ふたつめ、「末尾のあたり」と「末尾から30分前」は別の問題だということ。時刻で遡りたいのに、手がかりがバイト位置しかありません(第7回)。

みっつめ、色分けも保存された検索条件も、そこには無いこと。素早く末尾を見る手段はあっても、見方を持ち込む手段がありません第22回)。


3. 速さより「何回読むか」

状況

nginx のアクセスログから 5xx を追います。手順はいつもどおりです。

  1. grep で 5xx を数えて当たりをつける
  2. ビューアで同じファイルを開く
  3. ビューアの検索でその時刻へ行く
  4. 前後を読む

各手順は数十秒です。なのに、終わって時計を見ると1時間経っています。

なぜ起きるか

同じファイルを、何度も先頭から読んでいるからです。

50GB・950MB/s なら、1回の全走査は約54秒が物理的な下限です。手順1で1回、手順2で1回、手順3でもう1回読めば、それだけで約2分40秒。どれだけコマンドを磨いても、この回数は減りません。

実測でも同じ形になります。51GBで「探して、画面で読む」までの通し時間です。

51GB
klogg(開く52.55秒 + 検索55.59秒) 108.14秒

klogg は、開くのに1回、探すのにもう1回、ファイルを読みます。 1回あたりは930MB/s近くまで出ているので、遅いのではありません。読む回数が2回だから2倍かかる、というだけのことです。

ここが、単発コマンドの秒数比較では見えない部分です。grep 単体の速さを1割縮めるより、読む回数を2回から1回にするほうが効きます第15回)。

汎用ツールでの対処と限界

回数を減らす方向の定石は、小さくしてから渡すことです。

# 5xx だけ、原本の行番号を付けて抜く
grep -n -E ' (5[0-9]{2}) ' access.log > 5xx.txt

# 件数と時刻の分布を見る(ここまでで原本は1回読み)
awk '{print $4}' 5xx.txt | cut -c2-15 | uniq -c | sort -rn | head

# 気になった行の前後を、原本の座標のまま見る
awk 'NR>=41284440 && NR<=41284470 { printf "%d\t%s\n", NR, $0 }' access.log

うまくいきます。grep -n で行番号を持ち回るだけで、後段が原本を指し直せます。

限界は3つ。

ひとつめ、最後の awk でまた1回読むこと。行番号が分かっていても、そこへ跳ぶ手段が標準ツールにはありません。「もう少し前も見たい」と思うたびに54秒です。

ふたつめ、中間ファイルが増えること。5xx.txt は原本ではありません。これを配ると、どのファイルのどの範囲を抜いたものかが、すぐ分からなくなります(第24回)。

みっつめ、パイプでは回数が減らないこと。grep | awk | sort は1回読みですが、質問が変われば、また先頭から読み直します。 2問目・3問目のたびに全走査が走るのが、この経路の構造です。


4. 巨大テキストは、ログだけではない

状況

地図データを調べることになりました。OpenStreetMap のアメリカ全土(PBF形式・約11GB)を、osmium cat でXMLに展開します。約15分後、出てきたのがこれです。

us-260726.osm
258,679,440,228 byte  =  258.68 GB(240.9 GiB)

分割されていない、1本のXMLファイルです。 行数は 4,509,830,821行(45億行)。ログではないので、ローテートもされません。

なぜ起きるか

交換フォーマットは、人間が途中を覗く前提で設計されていないからです。

ログはローテートされ、日付で切られます。ところが地図データ、SQLダンプ、NDJSON、シミュレーション出力は、1本で完結していることに意味があるので、切られません。そして展開すると数百GBになります。

  • XMLは1要素1行になりがち — 45億行という数は、行が短いことの裏返しです
  • 専用ツールは「処理」が前提osmiumxmlstarletjq も、変換や抽出には強いのに、まず目で見るのには向きません(第9回
  • split が構造を壊す — バイト数で切れば、タグの途中で切れます

このサイズでの実測も出しておきます。macOS 実機で、258.68GB を全走査して索引を作り終えるまで 5分28秒(328秒)=約789MB/s。索引完了後の全文検索「New York」が 34.8秒 / 100,492件ヒットでした。

汎用ツールでの対処と限界

生のまま覗くなら、ストリームで流すのが基本です。

# 先頭だけ見て構造を確かめる
head -c 200000 us-260726.osm | xmlstarlet fo 2>/dev/null | head -60

# 特定のタグだけ数える(全走査1回)
grep -c '<way ' us-260726.osm

# 変換せずに目的地の見当をつける
grep -n -m 5 '"New York"' us-260726.osm

判断はできます。

限界は3つ。

ひとつめ、確認1回ごとに全走査が要ること。「行数を数える」だけで数分、質問が変わればまた数分です。

ふたつめ、jqosmium に渡す前の目視が抜けること。スキーマの推測を間違えたまま処理を走らせると、数十分後に気づきます。

みっつめ、PBFに戻れないこと。元の11GBは圧縮バイナリなので、テキストとして読むには展開が要り、展開すると258GBになります。「小さいまま読む」という選択肢が、この経路にはありません第8回)。


4つに共通していたもの

検証 出た数字 数字が言っていること 向きが変わる条件
48GBを開く 全域移動まで4分06秒/索引246秒 「対応容量」は作業開始時間を保証しない ファイルサイズ(小さければ差は消える)
51GBを開く 52.55秒=930MB/s(素読み約950MB/s) 待ちの大半は物理。速度では縮まない 設計(待たせるか、裏で作るか)
探して読むまで 108.14秒(開く+検索の2回読み) 秒数より読む回数が効く 質問の回数(1問なら差は小さい)
258.68GBの索引 328秒=約789MB/s/45億行 確認1回ごとに全走査が要る構造 2回目があるか(索引が残るか)

4つとも、下限を決めているのは同じ式です。

1回の全走査にかかる時間 = ファイルサイズ ÷ ディスクの読み出し速度。

50GB・950MB/s なら約54秒。この54秒は、どんなツールでも消えません。だから比較で意味を持つのは、次の3つを添えた数字だけです。

  • サイズ — 3GBの結果は50GBでは逆転します。メモリに収まるかどうかで別の競技になります
  • 回数 — 1問だけなら、索引を作る側が損をします。同じファイルに2回以上聞くときだけ得をします
  • ディスク — 内蔵SSDと外付けUSB SSDとネットワーク共有では、律速している場所そのものが違います(第17回

裏を返せば、勝てる余地は2か所しかありません。待たせないこと(走査の完了を待たずに全域を見せる)と、2回目を無料にすること(索引を残す)です。

冒頭の「上限は950MB/s」に戻ります。あれは負けの報告ではなく、この上限を前提にして何を設計するかという話でした。連載25回ぶん、扱ってきたのは全部その1点です。


使っている道具

私が開発している UwView(無料)は、巨大なテキストを開いた瞬間から表示・スクロール・検索できるビューアです。ファイル全体をメモリへ読み込まないので、RAM より大きいファイルでも開けます。索引はバックグラウンドで作られ、完成すると行番号が付きます。

  • 索引の完了を待たずに全域が見えます: 1章・2章に直接効きます。開いた直後に末尾へ移動でき、50%付近を確認でき、検索も始められます。258.68GB・45億行のXMLでも、索引作成が10%の時点で中身を読めています(258GBの実測記事
  • 同じ48GBでの比較: 索引作成74秒(同環境のEmEditor Professional 26.2.5 は246秒)、「東京」の全文検索12.5秒(同160秒)。Windows 11・Core i7-1165G7・RAM 16GB・内蔵SSD・x64版どうしでの測定例です。EmEditor 側は Large File option 未使用——有効にしたときの挙動は測っていません(48GB比較の実測記事
  • 色分けと検索結果の保存: 3章の5xx調査はここに乗ります。ステータスコードのプリセットで塊を目で追い、検索結果を原本の行番号付きで別ファイルへ出せます(5xx調査の記事
  • 処理はすべて手元のマシンで完結します。調査対象のファイルを外部のサービスへ送りません

ここから先が UwView Pro の領分です。

  • 索引と圧縮を .uwvz として保存します: 4章の「確認1回ごとに全走査」が消えます。一度開いたファイルは2回目以降行番号付きのまま瞬時に開き直せます(47.73GBのテキストで実測0.02〜0.07秒。特定環境での測定例です)
  • 保管サイズは約1/9になります: 258.68GB のファイルに対して .uwvz サイドカーは28.61GB でした。圧縮したまま検索できるので、展開して読む必要がありません(第8回
  • 検索は2回目以降、最大約9倍(圧縮キャッシュ経由・実測条件つき)。3章の「2問目・3問目のたびに全走査」が効かなくなるのはここです
  • 多段階検索で、絞り込んだ先も原本の行番号が残ります多段階検索の記事

正直に書いておきます。開くだけの速度では負けています。

  • 51.25GBを「ただ開く」だけは、v1.6.5 では klogg のほうが速いでした(52.55秒 対 100.6秒)。v1.6.6 で逆転しています——50.44秒(3GB 2.99秒・10GB 10.13秒も klogg より速い)。読み出しは 967/966/969 MB/s で、媒体の速度そのものです。通しも索引の完成を待たずに検索を始められるようになって 53.7秒——klogg の 108.14秒に対して約2倍、コマンドの uvf … -open(v1.6.6 で 50.76秒)とほぼ同じところまで来ました(差は約3秒)(v1.6.6 の実測
  • 初回の完全な読み込み・完全索引には物理時間がかかります。 258.68GBで328秒というのは、速いのではなく「走査しながら読める」だけです
  • 初回の検索は、生ファイルを1回走査します。 ただし v1.6.6 からは開くのと同時に走るので、開き終わってから改めて走査を待つわけではありません——50GB で通し 53.7秒です。瞬時に返るようになるのは2回目以降(圧縮キャッシュ経由)という点は変わりません
  • 構造の解析はしません。 XMLの整形やクエリ、JSONの抽出は xmlstarletjq の仕事で、置き換えるものではありません
  • 扱うのはテキストです。 PBFのような圧縮バイナリは対象外なので、展開が前提になります
  • 記事中の数字はすべて特定環境での測定例です。 同じ結果を保証するものではありません

コマンドラインから同じことをする

v1.6.0 で uvp コマンドが付きました(無料版には uvf)。GUI と同じ .uwvz を使うので、CLI で作った索引はそのまま GUI でも効きます。この記事の4章に対応させると、こう書きます。

# 1章: 開いて待つ前に、目的の語があるかだけ確かめる
uvp osm-japan.osm '東京'

# 2章: 末尾側の異常を、行番号付きで文脈ごと見る
uvp huge.log 'FATAL' -C 5

# 3章: 5xx の分布を1回読みで出す(2問目から索引が効く)
uvp access.log -uniq ' (5[0-9]{2}) ' -head 20

# 4章: 258GB級から該当箇所だけ書き出す
uvp us-260726.osm '"New York"' -C 2 -out survey/ny.txt.gz

# 行番号ジャンプ・色分け・文字コード切り替えは GUI 側の機能。続きはここで渡す
uvp access.log ' 503 ' -open

終了コードは grep と同じ 0=あり・1=なしに、2=上限に当たって打ち切った(既定は無制限。-limit N で上限を決めたときだけ)が加わります。if uvp access.log ' 503 '; then がそのまま書けます。

正直に書くと、1問目は uvp が ripgrep より少し遅くなります——.uwvz(圧縮+索引)を先に作るぶんで、50GB なら 59.0秒 対 54.9〜55.8秒=6〜7%。v1.6.6.1 で「作ってから探し直す」のをやめ、作りながら探すようにしたぶん 6.7秒 縮みました。効いてくるのは2問目からで、同じ 50GB の検索が 6.6秒、ripgrep の 55〜57秒に対して 8.4倍です。3GB のような小さいファイルでは同等——索引を引く意味が薄くなります。無料の uvf は、どのサイズでも ripgrep と同等です(実測記事。Mac M4・外付けUSB SSD・OpenStreetMap XML での測定例で、環境により異なります)。

この連載は今回で25回目、最後の回です。読んでいただきありがとうございました。次に巨大ファイルの比較記事を見かけたら、サイズ・回数・ディスクの3つが書いてあるかだけ確かめてみてください。それだけで、その数字が自分の現場に当てはまるかどうかが分かります。

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

リンク

  • 第1回: 巨大ファイルに沈む4つの定番ツールと、その先: https://uvp.y42u.net/blog/uwview-ps01-huge-file-tool-limits/
  • 第7回: タイムスタンプを武器にする4つの読み方: https://uvp.y42u.net/blog/uwview-ps07-timestamp-driven-triage/
  • 第8回: 圧縮保管と検索の両立: https://uvp.y42u.net/blog/uwview-ps08-compressed-archive-search/
  • 第9回: 構造化データを生のまま読む4場面: https://uvp.y42u.net/blog/uwview-ps09-read-raw-structured-data/
  • 第15回: コマンドライン職人芸の限界線4本: https://uvp.y42u.net/blog/uwview-ps15-cli-craft-limits/
  • 第17回: 開発環境とログの4つの摩擦: https://uvp.y42u.net/blog/uwview-ps17-dev-env-log-friction/
  • 第22回: 引き継げるチームの4つの工夫: https://uvp.y42u.net/blog/uwview-ps22-shareable-log-investigation/
  • 第24回: 原本に書き込まない4つの作法: https://uvp.y42u.net/blog/uwview-ps24-never-modify-the-original/
  • 48GB・8.92億行での比較(EmEditor との実測): https://uvp.y42u.net/blog/uwview-emeditor-48gb-comparison/
  • 258.68GB・45億行を開く(実測): https://uvp.y42u.net/blog/uwview-osm-usa-258gb/
  • アクセスログの5xxを1本の線でつなぐ: https://uvp.y42u.net/blog/uwview-access-log-5xx-workflow/
  • 3サイズでのベンチマーク: https://uvp.y42u.net/blog/uwview-pro-benchmark-3sizes/
  • 絞り込んだ先からさらに絞り込む(多段階検索): https://uvp.y42u.net/blog/uvp-drilldown-search/
  • uvp コマンドを付けました(ripgrep との実測比較): https://uvp.y42u.net/blog/uvp-cli-release-vs-ripgrep/
  • ソースコード(GitHub): https://github.com/amru195704/UwView

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


お願い
本記事の情報は参考目的で掲載しており、正確性・完全性を保証するものではありません。記載した実測値はすべて特定の1環境・1本のファイルでの測定例であり、同じ結果を保証するものではありません。ディスクの種類・接続方式・ファイルシステム・ページキャッシュの状態・同時に動いている他のプロセス・各製品のバージョンと設定により、結果は大きく変わります。他製品の数値は当方の環境で測定したものであり、それぞれの製品の性能を一般的に代表するものではありません。各製品には本記事で扱っていない機能・長所が多数あります。コマンド例は環境(GNU/BSD、awkgrepsplitxmlstarletosmium の実装差、シェルの種類)により調整が必要です。オプション名や既定値は必ず手元の man で確認してください。OpenStreetMap のデータは ODbL のもとで提供されており、利用の際はライセンス条件に従ってください。ログの取り扱い・持ち出しの可否は、所属組織および委託元の規程、ならびに適用される法令に従ってください。誤記・不正確な情報がございましたら、コメント欄よりご指摘いただければ、確認のうえ修正いたします。

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