gzip形式のままでもっと圧縮する

TL;DR

zopfliよりp7zip使おうぜ

初めに

ご存知の通り、gzipはデータの圧縮にRFC 1951のdeflateというアルゴリズムを使用している。実はdeflateを実装しているのはgzipだけではなく、ほかの実装を使うことで、gzipと同じファイルフォーマットでより高い圧縮率を得ることができる。

zopfli

そのような実装の一つがzopfliである。これはとても遅いが、gzipよりも高い圧縮率を得られる。gzipで最高の圧縮率のオプション-9を指定したときと比べてみよう。

まず、テキストファイルのサンプルとしてftp.jaist.ac.jpのhttpdのアクセスログの一部を用意した。サイズは1,979,738,541バイトである。計測にはAzureのD4s v3インスタンスのUbuntu 18.04.1上で行った。結果は以下の通り。

圧縮方法 結果 圧縮率 gzip比 時間 gzip比
gzip -9 171,156,866 8.65% 1.00 2分11秒 1.00
zopfli 162,630,441 8.21% 0.950 2時間48分29秒 76.6

zopfliは、圧縮率の高いPNGファイルを生成するために用いられることがあるので、もう一つのサンプルとして無圧縮画像ファイルを用意した。JPEGで1,847,165バイトの花の写真のファイルを、無圧縮TIFFに変換したものであり、サイズは35,426,773バイトである。結果は以下の通り。

圧縮方法 結果 圧縮率 gzip比 時間 gzip比
gzip -9 26,560,143 75.0% 1.00 1.44秒 1.00
zopfli 25,502,520 72.0% 0.960 1分52秒 78.0

どちらのサンプルを用いた場合でも、gzip -9に対して4~5%高い圧縮率を得るのに、75倍以上の処理時間が掛かる結果になっている。これが割に合うかというと微妙である。

zopfliはオプションで処理時間と圧縮率のバランスを調節できる。zopfliはLZ77を実行する際に、統計を取りながら何度も繰り返し実行して最適な結果を得ている。--iオプションを用いると、この繰り返し回数を指定できる。デフォルトは15回となっている。試しにワンパス、つまり--i1を指定すると以下の結果になる。

対象 結果 圧縮率 gzip比 デフォルト比 時間 gzip比 デフォルト比
テキストファイル 163,222,097 8.24% 0.954 1.00 38分7秒 17.5 0.226
画像ファイル 25,569,836 72.2% 0.962 1.00 45.6秒 31.8 0.407

デフォルトの場合と圧縮結果の差はほとんどないのに、処理時間は大幅に減っている。

zopfliは繰り返し回数を増やすと大幅に処理時間が増えるが、それに見合う結果が得られないことが多い。これらのファイルについてはワンパスで十分と言える。ただし、このわずかの差が決定的な差になるユースケースでは、繰り返し回数を増やす価値がある。

p7zip

ここで紹介するもう一つのdeflateの実装がp7zipである。p7zipはWindows用の7-Zipのコマンドラインバージョンである7za.exeを、Unix向けに移植したものだ。7-Zipは、主にLZAMA方式で圧縮する7z形式の書庫を扱うツールだが、deflateで圧縮することもできるし、gzip形式で出力することもできる。

たとえば、7zaでhogeをhoge.gzに最高の圧縮率で圧縮するときには、7za -tgzip -mx=9 a hoge.gz hogeのように実行する。7-Zipは独自に圧縮率の高いdeflateを実装していて、結果としてgzipよりも高い圧縮率が得られる。前掲の二つのサンプルについて計測すると以下のようになる。

対象 結果 圧縮率 gzip比 zopfliワンパス比 時間 gzip比 zopfliワンパス比
テキストファイル 166,770,822 8.42% 0.974 1.02 24分54秒 11.4 0.653
画像ファイル 25,518,673 72.0% 0.961 0.998 24.1秒 16.8 0.529

テキストファイルの処理は、ワンパスのzopfliより35%早いものの圧縮結果はやや劣る結果になった。画像ファイルについては、同zopfliよりも約半分の処理時間で、ほぼ変わらない結果が得られた。

まとめ

gzipよりも高い圧縮率が得られるdeflateの実装として、zopfliとp7zipを紹介した。ここでは、差を比べやすくするためにサンプルとして大きなファイルを選択した。そのため処理時間が到底受け入れられないものに見えたかもしれない。しかし、それほど大きなファイルでなければ、もともと高速なgzipに対しての10倍~20倍程度の処理時間は許容できることが多いはずだ。それならp7zipは試す価値があると思う。zopfliについては、処理時間に対して結果が見合わないように思う。

おまけ

ところで、圧縮率よりも速度が気になる場合には、zstdのdeflate実装を試すといいだろう。ログを gzip で圧縮しているなら zstd を導入しようにおいて、zstdのdeflate実装を利用すると、圧縮率はそそのままで圧縮で約10%、伸長で約40%もの高速化が図れることが紹介されている。