メインコンテンツまでスキップ

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

Sato Taichi

このたび 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 さんに煽られて書きました。

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