Google App Engineでシリアライズのチューニング
GAEアプリって結構Objectのシリアライズ使うことあるよね?(俺だけ?)
転置インデックスとか、ビュー(クエリの結果をまるごとシリアライズして1エンティティに保持してるやつ)とか、
結構いろいろと使ってるんだけど、微妙に遅いので速くならないかなーと。
Javaの標準シリアライズ以外に、シリアライズのライブラリって色々あるみたいです。
- Apache Thrift
- GoogleのProtocol Buffer http://code.google.com/intl/ja/apis/protocolbuffers/
- HadoopのWritable
- Message Pack http://msgpack.org/
等
んで、速そうな奴&簡単に使えそうな奴って事で、
HadoopのWritableと、Message Packを試してみました。
んで、肝心の速度は、
文字列の配列程度ならどれもそんな大差なかった。です…。
まぁ、そうかな。
んじゃ、オブジェクトは?
と思ったら、Message PackのオブジェクトシリアライズはGAEで動かないっぽい?
内部でJavassist使ってるらしくてエラーが。
※Javassist使わない方法もあるかもしれないけど、調べてない。
【修正】GAEで動きました。[twitter:@muga_nishizawa]さんありがとうございます。
現時点ではDate型対応してないっぽいのでlongに変換してシリアライズするのが良いと思います。
MessagePackの使い方についてはこちらの記事も参考になります。 http://d.hatena.ne.jp/viver/20101025/p1
ということで2つで比較すると、
Writableでシリアライズした方が倍以上速い感じです。
※どんなオブジェクトかによって差があるっぽいですが、複雑なオブジェクトほど、Writable有利な印象です。
ということで3つで比較すると、
予想通りMessagePack速いですが、Writableとそんな差は無い感じです。
多分扱うオブジェクトによって変わってくると思うので試してみてから判断した方が良いと思います。
今回使ったオブジェクトは、
@MessagePackMessage public class TestItem implements Serializable { public int intNum; public long longNum; public String string; public long date; }
シリアライズ方法は、
//シリアライズ public byte[] serializeByMessagePack(Object[] obj) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); Packer pk = new Packer(out); pk.pack(obj); return out.toByteArray(); } //つか、これで良かったみたい。 public byte[] serializeByMessagePack2(TestItem[] obj) throws IOException { return MessagePack.pack(obj); } //デシリアライズ public TestItem[] deserializeByMessagePack(byte[] bytes) throws IOException { MessagePackObject unpack = MessagePack.unpack(bytes); return unpack.convert(TestItem[].class); } //デシリアライズはこれだとなぜかorg.msgpack.MessageTypeExceptionになりました。 public TestItem[] deserializeByMessagePack2(byte[] bytes) throws IOException { InputStream in = new ByteArrayInputStream(bytes); Unpacker pac = new Unpacker(in); return pac.unpack(TestItem[].class); } //これもダメ。org.msgpack.MessageTypeExceptionです。 public TestItem[] deserializeByMessagePack3(byte[] bytes) throws IOException { InputStream in = new ByteArrayInputStream(bytes); Unpacker pac = new Unpacker(in); List<TestItem> tmp = new LinkedList<TestItem>(); for (MessagePackObject messagePackObject : pac) { tmp.add(messagePackObject.convert(TestItem.class)); } return tmp.toArray(new TestItem[0]); }
こんな感じです。
シリアライズのお手軽さで考えるとMessagePackの方がだいぶイケてますが、
GAEで使う場合の問題点として、初回の実行だけ若干時間がかかる事(数百ミリsec)。
これは実行時にコンパイルしている仕組み上仕方ないですが、通常のWebアプリと違ってSpin-upを考慮する必要があるGAEではちょっと問題があります。
ということで、やっぱり
Writableでいくかな