byte[] -> String 変換のパフォーマンス、とAndroidでCharsetDecoder#decode(ByteBuffer in).toString()が遅い件

Android上でMessagePackのベンチマークコードを動かしつつprofile取ってみたら、msgpack-java/src/main/java/org/msgpack/unpacker/StringAccept.java at master · msgpack/msgpack-java · GitHub辺り(CharsetDecoder#decode(ByteBuffer in).toString())でほとんどの時間が掛かっているなぁ、という感じ。

で、そもそも new String(byte[] byte, String charsetName) と CharsetDecoder#decode(ByteBuffer in).toString() でどれくらい性能があるのか気になったので計測してみた。確か、new String(...) の内部で CharsetDecoder#decode() してるというのは以前調べたことがあったので、今回はどの位違ってくるのか?という点に着目。

  public static void main(String[] args) throws IOException {
    String src = "あいうえおアイウエオアイウエオaiueoAIUEO";
    byte[] raw = src.getBytes();
    ByteBuffer byteBuffer = ByteBuffer.wrap(raw);
    Charset cs = Charset.forName("UTF-8");
    int until = 10000000;

    long start = System.currentTimeMillis();
    for (int i = 0; i < until; i++) {
      cs.newDecoder().decode(byteBuffer).toString();
    }
    System.out.println("CharsetDecoder.decode(): " + (System.currentTimeMillis() - start));

    start = System.currentTimeMillis();
    for (int i = 0; i < until; i++) {
      new String(raw, "UTF-8");
    }
    System.out.println("String(byte[]): " + (System.currentTimeMillis() - start));
  }

こんなのを書いて動かして見たところ...

CharsetDecoder.decode(): 720
String(byte[]): 2367

ざっと3倍位new String(byte[] byte, String charsetName)のほうが遅かった。OpenJDK 1.6でも大体同じ。

I/XXXX    (28542): CharsetDecoder.decode(): 4366
I/XXXX    (28542): String(byte[]): 1471

同等のコードをDalvik VM上で動かしたら(ループの回数は1/100に減らした)、結果が逆転した。ちょうど三倍程度、CharsetDecoder.decodeの方が遅くなった。

この辺がmsgpack-javaAndroid上で動かすと遅くなる件と関係あるのかも。ないのかも。

型変換テンプレートで変換する際、中間オブジェクトを生成しないようにした

Android上でJSONとMessagePackの簡単なベンチマークをとってみた - komamitsu.log の続き。

http://syuki.skr.jp/files/201204041/furuhashi-master-last-iso-pdfa.pdf を読んだところ、Unpacker.read(Class)は中間的な動的型付けオブジェクトを生成しないとのことなので、こちらを使うように修正してみた。

use unpacker.read(Class<T>) · ea24660 · komamitsu/AndroidMsgpackBenchmark · GitHub

Android2.2 Android4.2
Array S JSON 6421 2818
Msgpack 14636 1121
Array D JSON 1423 597
Msgpack 16284 6300
Msgpack: Unpacker.read(Class) 7458 4167
Map S JSON 3282 1894
Msgpack 19394 1117
Map D JSON 1044 614
Msgpack 17350 7754
Msgpack: Unpacker.read(Class) 8676 5223

結果は、デシリアライズの速度が前回のJSON:Msgpack=10~17:1という結果から, JSON:Msgpack=8:1と改善。

Android上でJSONとMessagePackの簡単なベンチマークをとってみた

使ったコードはこちら => https://github.com/komamitsu/AndroidMsgpackBenchmark/blob/master/src/com/komamitsu/android/msgpackexample/MsgpackExampleActivity.java

ベンチの概要としては、100byte x 50000の配列、100byte(value) x 30000の辞書をJSON, MessagePackでシリアライズ・デシリアライズしてどんなもんか見てみようかと。

  • Android2.2(IS06)の場合
I/MsgpackExampleActivity ( 8963): benchmarkJsonArray: serialize start
I/MsgpackExampleActivity ( 8963): benchmarkJsonArray: serialize end  :6421
I/MsgpackExampleActivity ( 8963): benchmarkJsonArray: deserialize start
D/MsgpackExampleActivity ( 8963): benchmarkJsonArray: size=50000
I/MsgpackExampleActivity ( 8963): benchmarkJsonArray: deserialize end  :1423
I/MsgpackExampleActivity ( 8963): benchmarkJsonMap: serialize start
I/MsgpackExampleActivity ( 8963): benchmarkJsonMap: serialize end  :3282
I/MsgpackExampleActivity ( 8963): benchmarkJsonMap: deserialize start
D/MsgpackExampleActivity ( 8963): benchmarkJsonMap: size=30000
I/MsgpackExampleActivity ( 8963): benchmarkJsonMap: deserialize end  :1044
I/MsgpackExampleActivity ( 8963): benchmarkMsgpackDynamicArray: serialize start
I/MsgpackExampleActivity ( 8963): benchmarkMsgpackDynamicArray: serialize end  :14636
I/MsgpackExampleActivity ( 8963): benchmarkMsgpackDynamicArray: deserialize start
I/MsgpackExampleActivity ( 8963): benchmarkMsgpackDynamicArray: deserialize end  :16284
I/MsgpackExampleActivity ( 8963): benchmarkMsgpackDynamicMap: serialize start
I/MsgpackExampleActivity ( 8963): benchmarkMsgpackDynamicMap: serialize end  :19394
I/MsgpackExampleActivity ( 8963): benchmarkMsgpackDynamicMap: deserialize start
I/MsgpackExampleActivity ( 8963): benchmarkMsgpackDynamicMap: deserialize end  :17350
I/MsgpackExampleActivity (20424): benchmarkJsonArray: serialize start
I/MsgpackExampleActivity (20424): benchmarkJsonArray: serialize end  :2818
I/MsgpackExampleActivity (20424): benchmarkJsonArray: deserialize start
D/MsgpackExampleActivity (20424): benchmarkJsonArray: size=50000
I/MsgpackExampleActivity (20424): benchmarkJsonArray: deserialize end  :597
I/MsgpackExampleActivity (20424): benchmarkJsonMap: serialize start
I/MsgpackExampleActivity (20424): benchmarkJsonMap: serialize end  :1897
I/MsgpackExampleActivity (20424): benchmarkJsonMap: deserialize start
D/MsgpackExampleActivity (20424): benchmarkJsonMap: size=30000
I/MsgpackExampleActivity (20424): benchmarkJsonMap: deserialize end  :614
I/MsgpackExampleActivity (20424): benchmarkMsgpackDynamicArray: serialize start
I/MsgpackExampleActivity (20424): benchmarkMsgpackDynamicArray: serialize end  :1121
I/MsgpackExampleActivity (20424): benchmarkMsgpackDynamicArray: deserialize start
I/MsgpackExampleActivity (20424): benchmarkMsgpackDynamicArray: deserialize end  :6300
I/MsgpackExampleActivity (20424): benchmarkMsgpackDynamicMap: serialize start
I/MsgpackExampleActivity (20424): benchmarkMsgpackDynamicMap: serialize end  :1117
I/MsgpackExampleActivity (20424): benchmarkMsgpackDynamicMap: deserialize start
I/MsgpackExampleActivity (20424): benchmarkMsgpackDynamicMap: deserialize end  :7754

眠いので結果に関しては整形して見やすくしたりしない方向で....
自分でも見づらかったので整形 (単位はmsec)。

Android2.2 Android4.2
Array S JSON 6421 2818
Msgpack 14636 1121
Array D JSON 1423 597
Msgpack 16284 6300
Map S JSON 3282 1894
Msgpack 19394 1117
Map D JSON 1044 614
Msgpack 17350 7754

あと、三回程度繰り替えしても大体同じ結果。



結果の概要:

まだあんまりMessagePackの仕様について把握して無いので、もうちょっと原理を抑えてから再度試みてみたい(そして眠りたい)。あと、別の要因がからんでいるかも、とか。

NewstreamをWebViewからListViewに変更

まとめ系ニュースを、さらにまとめて眺める系アプリ作った - komamitsu.log で作ったときはサーバー側でHTML作って、Android側ではWebViewで表示させていたのですが、どうにももっさりしていたので、えいやとListViewに書き換えました。

手元の古のIS06ではかなり画面の表示が高速化されたので嬉しい。

Newstream - Google Play の Android アプリ