ARM64_Lab

zlib レベル6が102.7 MB/sで圧縮率110.28倍だった

この記事の見出し
  1. 同じ bytes を標準ライブラリへ流す
  2. 速さと縮み方の表
  3. 予想と違ったところ
  4. 今回の失敗
  5. どう使い分けるか

6,642,000 bytes の入力を Python 標準ライブラリで圧縮すると、zlib-6 は102.7 MB/sで110.28倍、lzma は11.8 MB/sで1030.73倍になったが、この圧縮率は信用しすぎないほうがいい。測った入力は、自分のサイトのビルドスクリプトを400回くり返して作ったテキストで、同じ文字列が延々と並ぶ。冗長性が現実のファイルより強すぎるので、題材選びとしては失敗だった(数字が派手に出るほど、入力の作り方を疑うべきだった)。

それでも、まったく無意味というわけではなく、同じ入力を同じ Python 3.12.10 の ARM64 版で処理しているので、速度の序列は見られる。zlib のレベル差、bz2 の遅さ、lzma の圧縮率の出方は、手元の Surface Pro で実際に測った比較として残せる。

計測データは2026-06-13T20:17:52に取ったもので、2026年6月13日のこの1回分の測定を、そのままログとして使っている(別日に取り直した平均との差ではない)。

同じ bytes を標準ライブラリへ流す

Python の zlibbz2lzma をそのまま呼び、同じ bytes を3回ずつ圧縮した。入力はテキストなので、画像や既に圧縮済みのファイルは混ぜていなかったし、ここが今回の弱いところでもある(現実のフォルダからはかなり離れる)。

import bz2
import lzma
import zlib

cases = {
    "zlib-1": lambda data: zlib.compress(data, 1),
    "zlib-6": lambda data: zlib.compress(data, 6),
    "zlib-9": lambda data: zlib.compress(data, 9),
    "bz2-9": lambda data: bz2.compress(data, compresslevel=9),
    "lzma": lambda data: lzma.compress(data),
}

測った値は3回の実行時間、中央値、出力 bytes、入力 bytes からの ratio、そして MB/sで、MB/s は表にある値をそのまま使った(記事側で計算式を増やさないため)。自分で記事側では再計算しておらず、表にある値をそのまま使った(再計算で丸めがずれると、ログとして見返すときに面倒になる)。

今回の入力は6,642,000 bytes。400回リピートという人工的な作りなので、圧縮アルゴリズムにとってはかなり親切なデータになっている。普段扱う JSON ログでも似た繰り返しはあるが、ここまで同一パターンだけにはならない(だから ratio の絶対値は話半分で見る)。

速さと縮み方の表

3回分の runs_ms をそのまま表に置く。中央値だけ見るときれいに見えるが、zlib-1 でも45.1msから81.7msまで振れているので、1回だけの測定では危ない。

手法 runs_ms(3回) median_ms MB/s out_bytes ratio
zlib-1 63.31 / 45.1 / 81.7 63.31 104.9 829435 8.01
zlib-6 64.68 / 52.84 / 71.7 64.68 102.7 60230 110.28
zlib-9 101.81 / 109.57 / 144.55 109.57 60.6 54538 121.79
bz2-9 1014.56 / 1151.17 / 1019.37 1019.37 6.5 77144 86.1
lzma 624.62 / 562.59 / 564.61 564.61 11.8 6444 1030.73

input_bytes は6,642,000 bytes。out_bytes は lzma が6444 bytes まで落ちている。ratio だけ見れば1030.73倍で数字としては強烈だが、ここで喜ぶのは早く、実データが本当に1000倍以上に縮む、という話とは別だ。今回は同じビルドスクリプトを重ねているため、辞書に乗った後は同じものを何度も参照する形になる。現実のフォルダには PNG、WebP、zip、SQLite、ランダムに近いログの断片も混ざるので、そこではこの表の ratio は崩れるはずだ(この入力は圧縮器に親切すぎる)。

予想と違ったところ

目についたのは zlib-1 と zlib-6 の差だった。zlib-1 は104.9 MB/s、zlib-6 は102.7 MB/sなので、速度はほとんど同じだった。ところが ratio は8.01倍から110.28倍へ跳ねる。今回の表では、レベル1を選ぶ理由がかなり薄く見えた。

最初は、レベルを下げればはっきり速くなると思っていた。圧縮率を少し捨てて速度を取る、というよくある使い分けを想像していたからだ。実際に測ってみたら、この入力では速度の得が小さすぎる(レベルを下げた見返りが薄い)。出力は829435 bytesと60230 bytesで大きく違うのに、median_ms は63.31msと64.68msに収まっている。

zlib-9 も面白く、60.6 MB/sまで落ちるのに ratio は121.79倍で、zlib-6 の110.28倍から少し伸びるだけで、出力は60230 bytesから54538 bytesへ減る(待ち時間の増え方に比べると、得たものは小さく見える)。保存容量を削りたい場面なら意味はあるが、普段のビルド成果物やログを一時的に固める用途なら、個人的にはレベル6で止める。

bz2-9 はかなり厳しい結果になり、6.5 MB/sで zlib-6 の102.7 MB/sとは桁が違ううえ、ratio は86.1倍で lzma の1030.73倍には届かない(待つ理由を見つけにくい)。昔からある形式なので試したが、この入力では待つわりに報われにくい。

今回の失敗

今回の失敗は、測るデータを楽に作りすぎたこと。なぜそうなるのかは、表の lzma を見るとすぐ分かるし、測る前に気づいておきたかったところ。

自分のサイトのビルドスクリプトを400回つなげれば、同じ bytes 数で各手法を比べやすい。そう考えて試したものの、圧縮の題材としては単純すぎる。lzma の6444 bytesや1030.73倍という値は、アルゴリズムの実力というより、入力が作為的すぎた結果に見える。

この間違いに気づくまで、最初は「lzma が圧倒的に強い」とだけ書きそうになっていた。そこが危なく、測定は動いていて、エラーも出ていなかった(だから数字だけ見ると信じたくなる)。だからこそ、数字が派手なときほど入力を疑う必要がある、という反省。

切り分けとして、圧縮率の絶対値を信用しすぎるのは危ない。一方で、同じ入力を使った相対比較として、速度の順番はまだ使える。zlib が速く、bz2 が遅く、lzma は圧縮率寄り。このくらいの粒度なら、今回のログから読んでもよさそうに感じる。

どう使い分けるか

自分の運用では、まず zlib-6 を基準にする。今回の入力では102.7 MB/sで走り、out_bytes は60230 bytesまで落ちた。zlib-1 は104.9 MB/sなので速さの差が小さいわりに、out_bytes が829435 bytesまで増える。この差は予想と違ったので、今後の小さなアーカイブ処理でもレベル1を雑に選ぶ理由は薄い。

zlib-9 は、圧縮後のサイズをもう少しだけ削りたいときに限定する。109.57msまで待って54538 bytesにするか、64.68msで60230 bytesにするか。今回の数字だけなら、通常は後者で足りる。

lzma は、速度を捨ててでも1本の成果物を小さくしたいときに見る。11.8 MB/sなので、毎回のビルドに入れるには重い。bz2 は今回の範囲では選びにくい。6.5 MB/sで86.1倍なら、zlib-6かlzmaのどちらかに寄せたくなる。

次はやり直すつもりで、画像混じりのフォルダ、実際の JSON ログ、既に圧縮済みのファイルを含むデータで測ってみる。今回のログは、圧縮率の数字をそのまま信じるためではなく、測り方を間違えると見た目だけ強い結果が出る、という記録として残しておく。

a
arm64lab — 個人運営

Surface Pro 11th Edition(Snapdragon X Elite)を2025年5月から常用機にしている個人の記録です。ARM64 版 Windows で詰まったところと、その場で測った値をそのまま書き残しています。特定の企業・団体とは関係がなく、いかなる組織を代表する見解でもありません。