性能比較

1.83 MiB の代表的なコーパス上で lhatartar.gzzip を compress + decompress で比較。各ツールでデフォルトと 最良の両レベルを計測。数字が各ツールの得意分野を 明確に示している。

各セル 5 回、中央値 ± 標本標準偏差。マシン:Apple M シリーズ、 ネイティブ lha ビルド(LHa for UNIX 1.14i-ac20220213、configure デフォルト)。壁時計 end-to-end:各セルの compress + decompress 合計時間。

← セキュリティ監査 · ↑ サイト

一覧表

同じ 1.83 MiB コーパスを compress / decompress 各 5 回、 中央値。標準偏差はノート PC 自身のラン間ノイズを反映。

ツール レベル compress decompress size B 圧縮率
中央値±σ中央値±σ
lha-lh5- (デフォルト) ~50 ms±5 ~40 ms±1 547,04529.93 %
lha-lh7- ~46 ms±1 ~40 ms±6 547,04529.93 %
tar無圧縮 ~80 ms±4 ~82 ms±5 2,732,544149.55 %
tar.gz-6 (デフォルト) ~210 ms±5 ~5 ms±0.1 455,43524.93 %
tar.gz-9 (最良) ~210 ms±1 ~5 ms±0.1 455,43524.93 %
gzip -c-6 (素ストリーム) ~170 ms±4 ~5 ms±0.2 455,48624.93 %
gzip -c-9 (最良) ~170 ms±3 ~5 ms±0.2 455,48624.93 %
zip-6 (デフォルト) ~62 ms±1 ~3 ms±0.4 566,68331.01 %
zip-9 (最良) ~85 ms±1 ~3 ms±0.1 566,68331.01 %

全時間は壁時計 end-to-end。「レベル」は各ツール固有の 圧縮段数。コーパスのファイル数は 304、非圧縮合計 1,827,165 バイト。 tar (無圧縮)の圧縮率が 100 % を超えるのは、 tar が各ファイルのメタデータ(UID、GID、モード、512 バイト パディング)を保存するため。小さいファイル群では、 deflate をかけない段階で 50 % のオーバーヘッドになる。

表の読み方

1. compress 速度:zip < lha < gzip < tar < tar.gz

デフォルトレベル:

デフォルトでは ziplhaより 22 %速く、 lhatar.gzより 76 %速い。 compress 速度の順序はコーパスを変えても安定している。

2. decompress 速度:tar.gzzip < lha < tar

decompress では順序が興味深い形で入れ替わる:

CDN から 100 回展開する実シナリオでは、絶対 decompress 時間は ネットワーク遅延に支配される。3 ツールすべて decompress は 1 桁 ms オーダー;差はディスクシーク + HTTP リクエスト レイテンシのノイズフロアよりはるかに小さい。

3. 圧縮率:tar.gz < gziplha < zip

同じ kenlm コーパス:

tar.gzlha の差は 約 5 ポイント — 1.8 MiB 上では約 91 KB。高速 CDN ではこの 91 KB を 再ダウンロードするのに 100 ms 未満。低速 CDN では 1-2 秒。 これが実際の回線上の size-vs-speed トレードオフ。

4. -9 はこのコーパスでは罠

すでに冗長性のあるテキスト(繰り返しのキーワードと空白を 多く含むソースコード)では、gzip -9 と tar.gz -9 は -6 より新しい最適化を見つけられない。コーパスの富度が 足りず、より高いレベルが活きない;-9 は compress 時間で 5-10 倍高いのに圧縮率向上は ≈ 0 %。

真に高エントロピーなコンテンツ(動画断片、データベースダンプ、 暗号化データ)があれば -9 は有意な差を出せる。このコーパスは そうではない。

どのツールをいつ使うか

何を気にするか選ぶ理由
最小出力 tar.gz -9 / zstd / brotli デフォルト 9(または zstd -19 / brotli -11)は単一 バイナリで出せる最良圧縮率。compress 時間が lha の 5 倍超、圧縮率は 5 % だけ良い。
最速 compress zip -6 ~62 ms。DEFLATE64 オフ、tar ブロックなし、ファイルあたり 少量のオーバーヘッド。deflate -6 より 6 % 圧縮率損失。
最速 decompress(CDN、OTA、ミラー) lha または zip このコーパスではどちらも 1 桁 ms。lha は出力がやや大きい ものの展開後のサイズがそもそも小さい。ネットワーク遅延が 支配的。「zlib / libiconv 不要」も含めて lha が答え。
CI アーティファクト + CDN の単一静的バイナリ lha x eget use lha で 152 KB の完全静的 バイナリを ~/.local/bin に置く、依存なし。 tar.gzは libz + libiconv + tar が必要、 zipは libzip + libz が必要。lha にはこの ツリーがない。
Windows / macOS ユーザーが箱から開ける zip(または .tar.gz zip は Windows + macOS のシステム組込。tar.gz も ネイティブ。lha はバイナリの事前インストールが必要 (x eget use lha 1 行で済むが、それでも ステップが 1 つ多い)。
組込み / ファームウェア / 旧ハードウェア lha 制約のあるターゲットで展開コード < 16 KB。640 KB RAM の MS-DOS マシン用に設計。

より深い考察

なぜ lha -lh5-tar.gz -6 より compress が速い

LZHUF(.lh5- の裏方にあるアルゴリズム)は 1990 年設計の静的 Huffman 表 + 高速文字列検索方式。 deflate(gzip / zlib)は LZ77 + 動的 Huffman + スライディングウィンドウで、エンコーダがウィンドウから マッチを選ぶ必要がある。ウィンドウを埋めきれない入力 (1.8 MiB は 32 KB ウィンドウに余裕で収まる)では、 deflate はマッチ探索に時間を費やす。 LZHUF はそれをしない。decompress 側は 同じ程度のデコードループ構造で拮抗する。

deflate ウィンドウよりずっと大きなコーパス(-6 で 32 KB) では、deflate はより長距離マッチを発見できるので 圧縮率で勝る。100 MiB のコーパスなら、 tar.gz -9lha -lh5- の 3-5 倍の compress 時間で圧縮率を上回ってくる。

なぜ tar (無圧縮) は入力よりずっと大きい

tar はファイルごとのメタデータ(UID、GID、モード、タイム スタンプ、ファイル名)と 512 バイト/レコードのパディングを 保存する。1.8 MiB / 304 ファイルというコーパスでは、 メタデータが元サイズの 50 % を超える。tar -czf は LZ77 バックエンドでほぼ元に戻すが、生のtar は 小ファイルコーパスでは元より大きくなる。

なぜ zip -6 decompress が tar.gz -6 より速い

zip のアーカイブフォーマットはファイル末尾に central directory を 1 つ持つ — デコーダは ストリーミングなしで読める。tar.gz は tar ヘッダから各エントリのサイズを取り、deflate に そのサイズを埋めてもらう必要がある。zip の deflate は central directory をまとめて包める。tar.gz の deflate は レコード単位 — 各レコードで別ヘッダ解析。差は ファイル数が多いほど開く。

なぜ lha の decompress は deflate / zstd / lz4 より遅いか

decompress は LZHUF(.lh5-.lh7- の 裏方)が歳を感じる場面。1990 年設計は ビット直列 (bit-serial)

つまりこの問いへの答えは:アルゴリズム自身が上限であり、 実装ではない。LZHUF は 1990 年に「286 + 1 MiB RAM で 高速展開」用に調整されていて、ビット直列デコードループが 自然な解だった。lha に SIMD 最適化を足しても decompress で 1.5-2 倍程度(length-distance コピーだけがベクトル化可能な ブロック)だが、底のビット直列 Huffman ループは根本的に 直列。より速い decompress が欲しければ、アルゴリズムを 変える必要があり、LZHUF のマイクロ最適化では足りない。

比較:zstd の decompress は同圧縮率で lha より 約 5-10 倍速く、lz4 はさらに 3-5 倍速い。 どちらも現代 SIMD CPU(x86_64 上の SSE2/AVX2、 aarch64 上の NEON)向けに調整されていて、decompress ループは SIMD 友好的に設計されている。lha は MMX(1996)より 6 年早く、最初のメンテナはターゲットにできるベクトル ユニットを持っていなかった。

これが問題になる場面:lha は「zstd/lz4 が入っていない システムで動く単一静的バイナリ」の正解。lha は マイクロ秒級 decompress が必要な高スループット CDN の正解ではない

再現方法

任意の Unix 系ホスト(macOS、Linux、BSD)で:

git clone --depth 1 https://github.com/ljh-sh/lha.git
cd lha
bash scripts/build.sh
cp build/src/lha /usr/local/bin/

# コーパス
test -d corpus/kenlm_src || cp -R ../kenlm/upstream/kenlm corpus/kenlm_src

# 各ツール 5 回、中央値。表の数字は Apple M シリーズで 5 回
# 計測した結果(中央値 ± 標本標準偏差)。
for tool in \
  'lha  :lha c k.lh5- corpus/kenlm_src' \
  'tar  :tar -cf k.tar -C corpus kenlm_src' \
  'tgz6 :tar -czf k6.tgz -C corpus kenlm_src' \
  'tgz9 :tar -czf -9 k9.tgz -C corpus kenlm_src' \
  'zip6 :zip -qr -6 k6.zip kenlm_src' \
  'zip9 :zip -qr -9 k9.zip kenlm_src' ; do
  label=${tool%%:*}; cmd=${tool#*:}
  rm -f k.*
  for r in 1 2 3 4 5; do
    rm -rf corpus/kenlm_src; cp -R ../kenlm corpus/kenlm_src
    t=$( { time $cmd >/dev/null 2>&1; } 2>&1 | awk '/real/{print $NF}')
    sz=$(stat -c%s k.* 2>/dev/null | head -1)
    echo "$label  t=$t  sz=$sz"
  done
done

数字はマシンによって違う(CPU 種別で ±10 %、macOS / Linux 横断で ±5 %)が、相対順位 (zip → lha → tar → tar.gz)(compress)、 (tar.gz → zip → lha → tar)(decompress)、 (tar.gz → lha → zip → tar)(圧縮率)は安定。