jackson-module-kogera 2.19.0-beta25
にて、value class
のシリアライズ / デシリアライズに関連する処理でMethodHandle
を使うように変更しました。
github.com
この記事では、変更の効果が最低でどの程度かを確認した結果を紹介します。
value class
関連処理をMethodHandle
化する理由
Jackson
でvalue class
を違和感なく処理できるようにするため、value class
関連処理は多くのリフレクション呼び出しが必要であり、リフレクション呼び出しが連続することもしばしば有るためです。
例えばデシリアライズ時は、最低でも「プライマリコンストラクタ呼び出し -> box
化呼び出し」と2回のリフレクション呼び出しが必要です。
プロパティが非null
の場合、更にunbox
化呼び出しも追加で必要です。
シリアライズの場合も、box
化呼び出しとunbox
化で2回のリフレクション呼び出しが必要です。
これらはMethodHandle
化することで多くの改善が期待できます。
以下のリポジトリを使って比較を行いました。
github.com
MethodHandle
で効果的に高速化するためには、型を明示することが重要です。
jackson-module-kogera 2.19.0-beta25
では、ラップされる型がInt
, Long
, String
, (Java
)UUID
のいずれかだった場合に関して、型を明示的に扱うことによる最適化を行っています。
よって、この最適化の有無を比較する必要が有ります。
また、ベンチマーク内容は、Jackson
による処理の量をなるべく減らし、今回の高速化に関する効果が表れやすいようにする必要が有ります。
以上を考慮し、ベンチマーク内容は以下のようにしました。
- ベンチマークの入出力は基本的に1桁整数 + 最低限の
JSON
構造
- ベンチマーク対象となる型は1プロパティのみ持たせる
- 検証対象とする
value class
は、Int
をラップしたもの(= 型明示による最適化有り)と、Short
をラップしたもの(= 型明示による最適化無し)
最適化の効果はvalue class
の数が増えた分だけ高まるため、このベンチマークからは、value class
に関する処理が最低でどれだけ改善されたかが分かります。
結果
8ad900dをまとめたものです。
比が1より大きいほど改善されています。
シリアライズ
10/12ケースで改善が確認できました。
最も基本的なケースであるunbox.IntBenchmark.wrapped
(シンプルなvalue class
をプロパティに持つクラス)に関しては、約10%のスループット向上を記録しました。
key.jsonKey.ShortBenchmark.benchmark
とunbox.ShortBenchmark.direct
に関しては劣化が確認されましたが、それぞれ1%未満であり、誤差の範疇です。
|
2.19.0-beta24 |
2.19.0-beta25 |
2.19.0-beta25 / 2.19.0-beta24 |
key.jsonKey.IntBenchmark.benchmark |
1777332.255594 |
1817286.155664 |
1.022479702 |
key.jsonKey.ShortBenchmark.benchmark |
1831096.322124 |
1819773.928008 |
0.9938166038 |
key.unbox.IntBenchmark.benchmark |
1548940.949888 |
1557133.079293 |
1.005288858 |
key.unbox.ShortBenchmark.benchmark |
739703.867427 |
760804.578200 |
1.028525889 |
jsonValue.IntBenchmark.direct |
2892679.151030 |
2912412.745928 |
1.006821909 |
jsonValue.IntBenchmark.wrapped |
1663825.635318 |
1866756.436996 |
1.121966387 |
jsonValue.ShortBenchmark.direct |
2811182.009216 |
2904066.732081 |
1.033041163 |
jsonValue.ShortBenchmark.wrapped |
1670737.423967 |
1832761.453181 |
1.096977554 |
unbox.IntBenchmark.direct |
3179525.595621 |
3225851.588935 |
1.014570096 |
unbox.IntBenchmark.wrapped |
1874000.549577 |
2057832.237266 |
1.098095856 |
unbox.ShortBenchmark.direct |
3137818.450200 |
3135692.306827 |
0.9993224135 |
unbox.ShortBenchmark.wrapped |
1631790.050848 |
1727636.426984 |
1.058736953 |
デシリアライズ
8/10ケースで改善が確認できました。
最も基本的なケースであるbyPrimaryConstructor.IntBenchmark.wrapped
(シンプルなvalue class
をプロパティに持つクラス)に関しては、約7%のスループット向上を記録しました。
byJsonCreator.ShortBenchmark.direct
とbyPrimaryConstructor.IntBenchmark.direct
に関しては、それぞれ約4%の劣化が確認されました。
ただし、これらはvalue class
を直接デシリアライズする稀なケースです。
|
2.19.0-beta24 |
2.19.0-beta25 |
2.19.0-beta25 / 2.19.0-beta24 |
KeyBenchmark._int |
628928.592524 |
642907.263310 |
1.022226165 |
KeyBenchmark._short |
536357.711456 |
555365.450802 |
1.03543855 |
byJsonCreator.IntBenchmark.direct |
1784708.471187 |
1850169.260039 |
1.036678701 |
byJsonCreator.IntBenchmark.wrapped |
916055.903594 |
958713.142722 |
1.046566196 |
byJsonCreator.ShortBenchmark.direct |
1880281.625663 |
1809603.994811 |
0.9624111463 |
byJsonCreator.ShortBenchmark.wrapped |
921136.345195 |
935274.268244 |
1.01534835 |
byPrimaryConstructor.IntBenchmark.direct |
2147950.142659 |
2057745.453727 |
0.9580042911 |
byPrimaryConstructor.IntBenchmark.wrapped |
995537.946778 |
1064727.840156 |
1.069500006 |
byPrimaryConstructor.ShortBenchmark.direct |
1691792.929911 |
1739138.397988 |
1.02798538 |
byPrimaryConstructor.ShortBenchmark.wrapped |
904488.697510 |
963217.354551 |
1.064930228 |
シングルショット
比が1より小さいほど改善されています。
シリアライズ
11/12ケースで劣化、劣化しなかったケースもほぼ差が有りませんでした。
最も基本的なケースであるunbox.IntBenchmark.wrapped
に関して、劣化は2%未満でした。
|
2.19.0-beta24 |
2.19.0-beta25 |
2.19.0-beta25 / 2.19.0-beta24 |
key.jsonKey.IntBenchmark.benchmark |
184.561424 |
195.296498 |
1.058165318 |
key.jsonKey.ShortBenchmark.benchmark |
187.364271 |
194.738718 |
1.039358875 |
key.unbox.IntBenchmark.benchmark |
192.471979 |
192.413958 |
0.9996985483 |
key.unbox.ShortBenchmark.benchmark |
185.425115 |
186.525809 |
1.005936057 |
jsonValue.IntBenchmark.direct |
172.408964 |
176.969350 |
1.02645098 |
jsonValue.IntBenchmark.wrapped |
286.545790 |
295.294399 |
1.030531277 |
jsonValue.ShortBenchmark.direct |
175.135014 |
175.543538 |
1.002332623 |
jsonValue.ShortBenchmark.wrapped |
282.984258 |
295.457164 |
1.044076325 |
unbox.IntBenchmark.direct |
175.688984 |
177.968913 |
1.012977074 |
unbox.IntBenchmark.wrapped |
292.981319 |
297.085654 |
1.014008862 |
unbox.ShortBenchmark.direct |
176.957803 |
177.437413 |
1.002710307 |
unbox.ShortBenchmark.wrapped |
281.099359 |
288.922695 |
1.027831213 |
デシリアライズ
7/10ケースで劣化、劣化しなかったケースもほぼ差が有りませんでした。
最も基本的なケースであるbyPrimaryConstructor.IntBenchmark.wrapped
に関して、劣化は2%少しでした。
|
2.19.0-beta24 |
2.19.0-beta25 |
2.19.0-beta25 / 2.19.0-beta24 |
KeyBenchmark._int |
281.434946 |
280.577312 |
0.9969526386 |
KeyBenchmark._short |
277.767897 |
286.769142 |
1.032405635 |
byJsonCreator.IntBenchmark.direct |
269.644305 |
270.065927 |
1.001563623 |
byJsonCreator.IntBenchmark.wrapped |
305.317364 |
307.809560 |
1.008162641 |
byJsonCreator.ShortBenchmark.direct |
270.556615 |
275.870427 |
1.019640296 |
byJsonCreator.ShortBenchmark.wrapped |
308.366190 |
306.834562 |
0.9950330871 |
byPrimaryConstructor.IntBenchmark.direct |
275.885608 |
278.743274 |
1.010358155 |
byPrimaryConstructor.IntBenchmark.wrapped |
302.336269 |
309.165245 |
1.022587353 |
byPrimaryConstructor.ShortBenchmark.direct |
272.375393 |
269.656569 |
0.9900180998 |
byPrimaryConstructor.ShortBenchmark.wrapped |
297.234643 |
316.185681 |
1.063757837 |
全体を通した考察
スループットに関しては、想定通り基本的なケース全般で改善を確認できました。
特にシリアライズは10%、デシリアライズは7%程度と、Jackson
に関するその他処理が有る中ではそれなりの改善量が得られたことは良かったです。
ただし、恐らくベンチマーク負荷が軽すぎたため、型明示による最適化有無の差はそれ程確認できませんでした(これに関しては、逆に「何が何でも最適化しなければならないほどの差は無い」とも言えるでしょうか)。
シングルショットに関しては、初期化処理の量自体は増えているため、想定通りほぼ劣化となりました。
ただ、全体を通しても劣化幅は10%未満であるため、それ程酷くならなかった点は安心しました。
data class
との差について
data class
と比較した際のスコアは、残念ながらMethodHandle
化後も大きいままでした。
github.com
補足
経験上、恐らくLambdaMetaFactory
を使った方が高速ですが、以下の理由からJackson
ではLambdaMetaFactory
を使わない方針となっているため、試していません。
github.com
おまけ
ベンチマーク結果は以下に格納してあります。
一応比較用テンプレートも用意しているため、ローカルでの実行結果比較も可能です。
drive.google.com