Java用JSONパーザのベンチマークをした話

このたびJSONパーザのベンチマークを書きましたので、どうぞご査収下さい。

taichi/json-benchmarks

Java8がインストール済みの環境で、当該リポジトリをcloneして

./gradlew jmh

と実行する事で追試できます。

結果について

jackson-databindが最速なので殆どの皆様におかれましては安心して下さい。

特に驚きに値するような事は何も起きていません。

僕のマシンで実行した結果はこちらです。(エビデンスが無いとの指摘を受け追記しました。)

結構な長文になってしまいましたので、マニアな皆様だけが続きを読んで下さい。

課題設定について

まず、おおまかな課題設定について説明します。

  • JMHを使った本格的なベンチマークを書く
  • Java用JSONパーザのショーケースを作る事で使い易いものを明らかにする
  • 結局どのパーザが速いのか調べる

JMHについて

そもそもがJavaのマイクロベンチマークというのは極めて趣味的なものであって、多くの人にとって余り益のない行為です。

一方で、ベンチマークプログラムを書く事は実行ランタイムに対する不必要な程の詳細な理解を得られる数すくない手段の一つでもあります。

また、正しいベンチマークプログラムを書くのは非常に難しいものです。 特にJVMには非常に強力な最適化の仕組みがありますので、それらの仕組みを目的通りに動かしたり止めたりするのは極めて難しいと言えます。

そこで利用できるベンチマークライブラリがOpenJDKの一部として公開されているJMHです。

JMHは非常に便利ですがドキュメントが壊滅的です。僕が参考にしたサイトは以下の4つです。

JMHのサンプルコードは単一の機能を使うという意味では分かり易いのですけども、充実したサンプルコードという意味では、GS Collectionsのベンチマークがオススメです。僕の観測範囲ではGS Collectionsが一番JMHを使っています。

JMHは基本的にMavenからの利用を想定しています。

一応、Gradleのプラグインも公開されているのですが細かい所でドキュメントやサンプルが無いのでコードを読みながら使わざるを得ません。また、非常に残念な事にJMHのGradleプラグインは、ベンチマークプログラムをIDE書く事を想定した作りになっていません。

最初の課題として

  • GradleからJMHをカジュアルに動かせるようにする
  • Gradleのeclipseタスクやideaタスク等からIDE用設定ファイルを適切に生成できるようにする

の二つを解決しました。その結果が、

です。eclipseではAnnotation Processor用の設定ファイルを生成するのが非常に面倒なのですけども、tbroyer/gradle-apt-pluginを使うと簡単に設定できます。尚、Android用のプラグインはhvisser/android-aptです。

これによって、eclipse上からmainメソッドを持つクラスを指定してJavaアプリケーションとしてベンチマークを動かせるようになりました。

最近のJSONパーザについて

今回ベンチマーク対象としたライブラリはこれらです。

サーバアプリケーションを実装するにあたって、どのJSONパーザを使うかというのは僕にとって一つの大きな課題です。歴史と実績のあるJacksonを使っておけば、概ね問題ないのですけども、本当にそれで良いのかはいつも考えています。

特に、AutoValueやImmutables、Lombokのような自動生成系の安定して動作するライブラリが登場してきたので、それとJacksonの相性はどうなんだろう?とか、

LoganSquareやig-json-parserのようにAnnotationProcessorベースで処理コストをある程度コンパイル時に払うタイプのパーザも現れました。

尚、現状のベンチマークではウォーミングアップを丁寧にやっているので初期起動コストの差分は全く評価出来ていません。

結果の検証

今現在のベンチマークでは、学習成果と思いつきをコードに落とし込んだだけの状態なので適切な結果の検証できる状態にはありません。

ぱっと見Jacksonを普通に使うのが最速です。

出自を考えれば当たり前なのですけどもGSONとMoshiは殆ど同等です。

ImmutablesとLoganSquareは、Jacksonの低レベルなAPIを直接使うコードを自動生成するのでもっと速くても良いように思うのですけども、そういう結果になっていません。

AutoValue+Jacksonのシリアライズがやけに速いのですが、多分これは僕の書いたコードに問題があるように思います。原因は調査出来ていません。
issue#1で指摘を受け、問題は解決しました。シリアライズしている対象が少ない為に高速に処理出来ていたようです。

最後に

JVM上で動作するシリアライザのちゃんとしたベンチマークを実行したいのでしたら、

がオススメです。

そもそも何でベンチマークし始めたのか

LoganSquareのベンチマークデモがAndroidアプリなのですぐに動かせなくてムキーってなった事。

Immutablesのベンチマークデモのコードが明らかにおかしいので修正しようと思ったけどディレクトリ構成はおかしいし、依存してるJMHのバージョンはアホ程古いしで、やってられっかーってなった事。

Boonのベンチマークの言ってる事が凄すぎるので追試したいと数年間思ってた事。

の3つが重なった結果、ちょっと頑張ってみたという感じです。

謝辞

このエントリは @nahi さんに煽られて書きました。

この一言が無ければ、当エントリは陽の目を見なかった事でしょう。本当にありがとうございます。