Snappy servay

Post on 22-Apr-2015

3.381 views 1 download

description

 

Transcript of Snappy servay

google-snappyを調べてみた 町永圭吾 (machy)

google-snappyとは

•  snappyは圧縮/伸張ライブラリです。snappyは圧縮率を最大にすることや、ほかの圧縮ライブラリと互換であることを目指していません。代わりにそれなりの圧縮率で非常に高速であることを目指しています。例えば、zlibの最高速モードと比較して、圧縮ファイルは20~100%大きくなりますが、殆どの入力に対して1桁速い速度になります。(詳細は下記”Performance”参照)

Snappy is a compression/decompression library. It does not aim for maximum compression, or compatibility with any other compression library; instead, it aims for very high speeds and reasonable compression. For instance, compared to the fastest mode of zlib, Snappy is an order of magnitude faster for most inputs, but the resulting compressed files are anywhere from 20% to 100% bigger. (For more information, see "Performance", below.)

READMEの冒頭から引用

まずは動かしてみる

• 本資料で使用したバージョンは1.0.3です • ダウンロード

http://code.google.com/p/snappy/ •  google-gflagsもダウンロード これはコマンドラインオプションの処理ライブラリです なくてもビルドはできますが、テストツールがコマンドラインからのオプションを全く受け付けない状態になります。

•  google-gflags, google-snappyの順番にビルド そのままビルドするとテストツールが WARNING: Compiled with assertions enabled, will be slow. というメッセージを出してくるので、一応アサートを切ってみます ./configure CXXFLAGS=“-g -O2 –DNDEBUG” --with-gflags --with-gflagsをつけると、gflagsが有効化できなかったときにconfigureが停止します。必要に応じてgflagsが有効になるようにオプションを追加。

snappy_unittestの使い方 • ビルドが成功すると、snappy_unittestというコマンドラインツールが生成されます

• マイクロベンチマークの実行 ./snappy_unittest

• 圧縮する ./snappy_unittest -run_microbenchmarks=false -write_compressed aaa.txt 圧縮ファイル aaa.txt.comp が生成されます

• 解凍する ./snappy_unittest -run_microbenchmarks=false -write_uncompressed aaa.txt.comp 圧縮前と同等のファイル aaa.txt.comp.uncomp が生成されます

•  zlibと比較する ./snappy_unittest -run_microbenchmarks=false -zlib testdata/*

圧縮テスト用のファイル

•  snappyには、圧縮テスト用のファイルが付属していました。 そのうち、以下のものを評価に使ってみました。

ファイル名 内容 サイズ

alice29.txt 不思議の国のアリス (英語) 149KB html_x_4 400KBのHTML 400KB urls.10K 1万行のURLリスト 686KB

baddata1.snappy 圧縮しにくいデータ(?) 27KB house.jpg 家の写真 124KB

zlibとの圧縮性能の比較結果 ファイル名 圧縮方法 圧縮速度 伸張速度 圧縮後サイズ

alice29.txt

snappy 99.1 MB/s 293.3 MB/s 59.8 %

zlib fastest 20.7 MB/s 81.5 MB/s 42.8 %

zlib default 6.6 MB/s 90.4 MB/s 35.8 %

html_x_4

snappy 230.2 MB/s 557.9 MB/s 23.6 %

zlib fastest 45.6 MB/s 154.5 MB/s 16.5 %

zlib default 20.7 MB /s 177.7 MB/s 13.0 %

urls.10K

snappy 132.6 MB/s 411.2 MB/s 50.9 %

zlib fastest 24.7 MB/s 94.8 MB/s 36.1 %

zlib default 12.2 MB/s 102.4 MB/s 31.7 %

baddata1.snappy

snappy 137.5 MB/s 1068.7 MB/s 97.0 %

zlib fastest 12.3 MB/s 57.0 MB/s 84.1 %

zlib default 10.8 MB/s 58.9 MB/s 83.4 %

house.jpg

snappy 933.7 MB/s 7271.6 MB/s 99.9 %

zlib fastest 11.9 MB/s 89.6 MB/s 99.6 %

zlib default 11.5 MB/s 122.4 MB/s 99.6 %

zlibとの圧縮性能の比較結果 ファイル名 圧縮方法 圧縮速度 伸張速度 圧縮後サイズ

alice29.txt

snappy 99.1 MB/s 293.3 MB/s 59.8 %

zlib fastest 20.7 MB/s 81.5 MB/s 42.8 %

zlib default 6.6 MB/s 90.4 MB/s 35.8 %

html_x_4

snappy 230.2 MB/s 557.9 MB/s 23.6 %

zlib fastest 45.6 MB/s 154.5 MB/s 16.5 %

zlib default 20.7 MB /s 177.7 MB/s 13.0 %

urls.10K

snappy 132.6 MB/s 411.2 MB/s 50.9 %

zlib fastest 24.7 MB/s 94.8 MB/s 36.1 %

zlib default 12.2 MB/s 102.4 MB/s 31.7 %

baddata1.snappy

snappy 137.5 MB/s 1068.7 MB/s 97.0 %

zlib fastest 12.3 MB/s 57.0 MB/s 84.1 %

zlib default 10.8 MB/s 58.9 MB/s 83.4 %

house.jpg

snappy 933.7 MB/s 7271.6 MB/s 99.9 %

zlib fastest 11.9 MB/s 89.6 MB/s 99.6 %

zlib default 11.5 MB/s 122.4 MB/s 99.6 %

テキストの圧縮では、圧縮速度でzlib fastest(level=1)の5倍以上 伸張速度が3.5倍以上高速。

圧縮時のサイズは1.2~1.4倍程度の大きさ。

zlibとの圧縮性能の比較結果 ファイル名 圧縮方法 圧縮速度 伸張速度 圧縮後サイズ

alice29.txt

snappy 99.1 MB/s 293.3 MB/s 59.8 %

zlib fastest 20.7 MB/s 81.5 MB/s 42.8 %

zlib default 6.6 MB/s 90.4 MB/s 35.8 %

html_x_4

snappy 230.2 MB/s 557.9 MB/s 23.6 %

zlib fastest 45.6 MB/s 154.5 MB/s 16.5 %

zlib default 20.7 MB /s 177.7 MB/s 13.0 %

urls.10K

snappy 132.6 MB/s 411.2 MB/s 50.9 %

zlib fastest 24.7 MB/s 94.8 MB/s 36.1 %

zlib default 12.2 MB/s 102.4 MB/s 31.7 %

baddata1.snappy

snappy 137.5 MB/s 1068.7 MB/s 97.0 %

zlib fastest 12.3 MB/s 57.0 MB/s 84.1 %

zlib default 10.8 MB/s 58.9 MB/s 83.4 %

house.jpg

snappy 933.7 MB/s 7271.6 MB/s 99.9 %

zlib fastest 11.9 MB/s 89.6 MB/s 99.6 %

zlib default 11.5 MB/s 122.4 MB/s 99.6 %

圧縮しにくいデータでは、snappyは顕著に高速。 これは、圧縮しにくいデータの圧縮を諦めるような

仕組みになっているため。

lzoとの圧縮性能の比較結果 ファイル名 圧縮方法 圧縮速度 伸張速度 圧縮後サイズ

alice29.txt snappy 85.9 MB/s 259.7 MB/s 59.8 %

lzo 90.6 MB/s 178.2 MB/s 57.8 %

html_x_4 snappy 206.7 MB/s 463.1 MB/s 23.6 %

lzo 203.3 MB/s 421.6 MB/s 21.8 %

urls.10K snappy 119.4 MB/s 363.2 MB/s 50.9 %

lzo 125.3 MB/s 308.5 MB/s 49.3 %

baddata1.snappy snappy 109.6 MB/s 1048.1 MB/s 97.0 %

lzo 353.4 MB/s 2267.1 MB/s 100.4 %

house.jpg snappy 846.4 MB/s 6642.0 MB/s 99.9 %

lzo 672.6 MB/s 2024.4 MB/s 100.3 %

最近hadoopで使われているlzoとの比較 概ねsnappyと同じような傾向を示すが、伸張速度はsnappyの方が速い。

圧縮速度と圧縮率はlzoの方がわずかによい。

※以上、あくまで私の環境での実験結果

基本的な仕組み snappyはzlibなどと同じで基本的には辞書式のようです

入力

zlib(deflate)

辞書式符号化 ハフマン符号化 出力

入力

snappy

辞書式符号化

リテラルはそのまま 参照の長さ・距離は ちょっと圧縮

出力

のちほど

辞書式符号化

HAHAHAHAHA...

HA[2,8]... コピー先を繰り返しコピーするような指定でもよい

government_of_the_people,_by_the_people,_for_the_people

government_of_the_people,_by[15,13]for[16,11]

15文字前から13文字

ハフマン符号化

0 0 0 1 0 0 1 1 1 0 0 0 1 1 0 0 0 0

0 0

0 1

1 0

1 1

0

1 0

1 1 0

1 1 1

0 1 1 0 0 1 0 1 1 1 0 1 0

18bit

0 0

15bit たくさん現れる記号に短い符号長を与えることで圧縮できる

snappyの符号化

00 111111

00 111110

00 111101

00 111100

00 000000

XXXXXXXX

XXXXXXXX XXXXXXXX

XXXXXXXX XXXXXXXX XXXXXXXX

LITERAL (1byte)

LITERAL (61byte)

LITERAL (~62+255byte)

LITERAL (~63+65535byte)

LITERAL

01 XXXX XXXXXXXX XX

10 XXXXXX XXXXXXXX XXXXXXXX

11 XXXXXX XXXXXXXX XXXXXXXX XXXXXXXX

LITERAL・・・参照できない データをそのまま保持する

LITERALの長さ

参照して コピーする (4byte以上)

コピーする 長さ コピー元の位置

コピーする長さ 4~11byte

~64byte

~64byte

snappyの符号化

00 111111

00 111110

00 111101

00 111100

00 000000

XXXXXXXX

XXXXXXXX XXXXXXXX

XXXXXXXX XXXXXXXX XXXXXXXX

LITERAL (1byte)

LITERAL (61byte)

LITERAL (~62+255byte)

LITERAL (~63+65535byte)

LITERAL

01 XXXX XXXXXXXX XX

10 XXXXXX XXXXXXXX XXXXXXXX

11 XXXXXX XXXXXXXX XXXXXXXX XXXXXXXX

LITERAL・・・参照できない データをそのまま保持する

LITERALの長さ

参照して コピーする (4byte以上)

コピーする 長さ コピー元の位置

コピーする長さ 4~11byte

~64byte

~64byte

現れやすい数値には短い符号長が割り当てられるようになっている

また、殆どByte単位の処理なので高速に行える

snappyの辞書参照元の探し方(1) 入力データを最大16KBのfragmentに分割します

入力データ

fragment (16KB) fragment (16KB) fragment

fragmentごとに個別に圧縮処理される

snappyの辞書参照元の探し方(2) ハッシュテーブル(通常8192エントリ)を使って、4byte以上マッチする位置を探します

government_of_the_people,_by_the_people,_for_the_people

4byte窓でハッシュ値を計算し、ハッシュテーブルを更新します

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 6 0 4 1 13 5 8 10 11 3

位置13 : Hash(“f_th”)=7 更新

government_of_the_people,_by_the_people,_for_the_people

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 6 0 4 1 13 14 5 8 10 11 3

位置14 : Hash(“_the”)=9 更新

snappyの辞書参照元の探し方(3)

government_of_the_people,_by_the_people,_for_the_people

既にハッシュテーブルに登録がある場合は、一致長を求めます

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 6 23 0 4 27 26 22 13 24 14 25 15 10 28 16 20

位置29 : Hash(“_the”)=9

government_of_the_people,_by_the_people,_for_the_people 一致長=13

一致しなかったところまではリテラルとして出力される

[government_of_the_people,_by][15,13]

続きから処理

government_of_the_people,_by_the_people,_for_the_people

位置32

ハッシュ関数はこんなかんじ

static inline uint32 HashBytes(uint32 bytes, int shift) { uint32 kMul = 0x1e35a7bd; return (bytes * kMul) >> shift; }

ハッシュ関数はこんなかんじ

static inline uint32 HashBytes(uint32 bytes, int shift) { uint32 kMul = 0x1e35a7bd; return (bytes * kMul) >> shift; }

#if defined(__i386__) || defined(__x86_64__) || defined(__powerpc__) #define UNALIGNED_LOAD32(_p) (*reinterpret_cast<const uint32 *>(_p)) #else inline uint32 UNALIGNED_LOAD32(const void *p) { uint32 t; memcpy(&t, p, sizeof t); return t; }

このハッシュ関数は4byte分を一括処理する。 4byte境界でないメモリアドレスから32bit整数レジスタに高速にロードできることを 期待している(そうでないCPUアーキテクチャにも対応している)

ハッシュ関数はこんなかんじ

static inline uint32 HashBytes(uint32 bytes, int shift) { uint32 kMul = 0x1e35a7bd; return (bytes * kMul) >> shift; } shiftは剰余演算の代わり ハッシュテーブルが8192エントリ(2の13乗)だったら、32-13=19 これで計算結果は8192未満に収まっている

参照元探しのスキップ なかなか参照すべきデータが見つからないときは、徐々に探し方が荒くなる。 これはJPEGなど圧縮しにくいデータの処理を高速にするため。 コメントで、この処理は圧縮可能なデータには僅かな損失しか与えない (~5% performance, ~0.1% density)と記述されている

...a93ecm2k39cn10xi10chakegueks16krpqw2453maheggubz...

...a93ecm2k39cn10xi10chakegueks16krpqw2453maheggubz...

...a93ecm2k39cn10xi10chakegueks16krpqw2453maheggubz...

...a93ecm2k39cn10xi10chakegueks16krpqw2453maheggubz...

前回のマッチから32回見つからないと移動幅が2になる

さらに32回見つからない毎に移動幅が1ずつ増える

16KB全くマッチしなかった場合で、比較回数は約1008回になる

この参照元探しの特徴

• 最長マッチは探さない。一番近い4byte以上のマッチを見つけたら、それを使うことにする

• ハッシュが衝突したら、あるはずのマッチがみつからないこともある

• メモリ消費量を抑えている。CPUのキャッシュを意識している?

• 圧縮しにくいデータに対して処理をスキップするような仕組みになっている(前述)

その他高速化のために

• 圧縮時に圧縮後のサイズは分からないが、最悪ケースでのサイズをあらかじめ確保し、途中でメモリの再確保が起こらないようになっている。 (これはzlibの場合でもdeflateBound(),compressBound()を用いることで同様のことができる)

まとめ

•  snappyはハフマン符号を使わずほぼ辞書式符号化のみ、参照の長さ・距離の処理も簡素なビット演算なので速い

• 辞書参照元のデータの探し方が適当なので速い •  16KBのfragmentごとの処理になっていること、ハッシュテーブルが小さいことでCPUキャッシュを有効活用しているから速い?

• 他にも速い理由があるかも? (zlibが読み解けてなくて、うまく比較できなかったのが悔やまれる)