snappy servay

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

Upload: keigo-machinaga

Post on 22-Apr-2015

3.386 views

Category:

Technology


1 download

DESCRIPTION

 

TRANSCRIPT

Page 1: Snappy servay

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

Page 2: Snappy servay

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の冒頭から引用

Page 3: Snappy servay

まずは動かしてみる

• 本資料で使用したバージョンは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が有効になるようにオプションを追加。

Page 4: Snappy servay

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/*

Page 5: Snappy servay

圧縮テスト用のファイル

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

ファイル名 内容 サイズ

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

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

Page 6: Snappy servay

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 %

Page 7: Snappy servay

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倍程度の大きさ。

Page 8: Snappy servay

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は顕著に高速。 これは、圧縮しにくいデータの圧縮を諦めるような

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

Page 9: Snappy servay

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の方がわずかによい。

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

Page 10: Snappy servay

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

入力

zlib(deflate)

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

入力

snappy

辞書式符号化

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

出力

のちほど

Page 11: Snappy servay

辞書式符号化

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文字

Page 12: Snappy servay

ハフマン符号化

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 たくさん現れる記号に短い符号長を与えることで圧縮できる

Page 13: Snappy servay

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

Page 14: Snappy servay

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単位の処理なので高速に行える

Page 15: Snappy servay

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

入力データ

fragment (16KB) fragment (16KB) fragment

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

Page 16: Snappy servay

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 更新

Page 17: Snappy servay

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

Page 18: Snappy servay

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

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

Page 19: Snappy servay

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

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アーキテクチャにも対応している)

Page 20: Snappy servay

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

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

Page 21: Snappy servay

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

...a93ecm2k39cn10xi10chakegueks16krpqw2453maheggubz...

...a93ecm2k39cn10xi10chakegueks16krpqw2453maheggubz...

...a93ecm2k39cn10xi10chakegueks16krpqw2453maheggubz...

...a93ecm2k39cn10xi10chakegueks16krpqw2453maheggubz...

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

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

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

Page 22: Snappy servay

この参照元探しの特徴

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

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

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

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

Page 23: Snappy servay

その他高速化のために

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

Page 24: Snappy servay

まとめ

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

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

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