PBZIP2 is a parallel implementation of the bzip2 block-sorting file compressor that uses pthreads and achieves near-linear speedup on SMP machines.
bz2は圧縮後のサイズが小さくていいんだけど、ちょっと遅いのが玉に瑕、でした。pbzip2はブロックごとのソートを複数のCPUで並列処理する、というすごい素直な実装のようです。pbzip2で圧縮したものはbzip2で普通に解凍できるとのこと。
てすと
ということでさっそくテスト。
環境は cpu: Core Duo T2500 2GHz, mem: 2GB, Debian etch, gzip 1.3.5, bzip2 1.0.3, pbzip2 0.9.6 で、15MBくらいのファイル x 8 = 合計115MB を圧縮してみました。ファイルの中身はアクセスログ的なもので、けっこう似たようなことがたくさん書いてあります。
種類 | 圧縮後のサイズ | user | cpu | total | メモリ使用量 | 解凍時total |
---|---|---|---|---|---|---|
gzip | 27MB | 7.52s | 99% | 7.674s | 0.6M | 1.622s |
bzip2 | 19MB | 45.17s | 99% | 45.433s | 6.9M | 12.364s |
pbzip2 | 19M | 53.08s | 193% | 27.994s | 18M | 8.820s |
pbzip2 -r | 19M | 52.85s | 196% | 27.017s | 42M | 12.834s |
ちなみに /usr/bin/time で1回計測しただけ、メモリはtopでRSS目視の最大値です…。
pbzip2 の "-r" は
-r Read entire input file into RAM and split between processors
だそうです。試しにcatで全ファイルをつなげて見たところ、RSSは145Mまで行きましたが、時間はあんま変わりませんでした。もっとどでかいファイルじゃないと意味ないかな。
phpではダメ!!
README見ると、ちょっと不安なことが書いてあります。
If you are writing software with libbzip2 to decompress data created
with pbzip2, you must take into account that the data contains multiple
bzip2 streams so you will encounter end-of-stream markers from libbzip2
after each stream and must look-ahead to see if there are any more
streams to process before quitting. The bzip2 program itself will
automatically handle this condition.
単純にファイル分割して圧縮するだけみたいなので、こういうことになっちゃうんですね。あと、stdinとかpipeとかで入力も(まだ?)できないそうで。
で、phpはどうなのかな、と思って試してみた。
% ls -l all.txt -rw-r--r-- 1 ichii386 ichii386 115M Nov 3 05:42 all.txt % php -v PHP 5.2.4 (cli) (built: Oct 22 2007 23:06:00) Copyright (c) 1997-2007 The PHP Group Zend Engine v2.2.0, Copyright (c) 1998-2007 Zend Technologies % php -r "echo file_get_contents('compress.bzip2://./all.txt.bz2');" > all2.txt % ls -l all2.txt -rw-r--r-- 1 ichii386 ichii386 900K Nov 3 05:58 all2.txt
おい、900Kってなんだよ!!
ようするに、デフォルトのブロックサイズ(bzip2とおなじ"-9"で変えられる)そのもので、まさにおっしゃる通りになったわけでした。php のえらい人、どなたか直してもらえませんかねぇ…。
追記@30分後
ext/bz2/bz2.c を読んだ感想。なんというか、BZ2_bzread とか使ってる時点でそういうことは想定してませんよ、的なかんじなのかも。BZ2_bzRead を使って BZ2_bzGetUnused をちゃんとやるように大幅に書き換えないとダメそうでした。