Inlining or not?
前回のエントリで、インライン化についてちょっと触れました。
ちなみにCharacter#charCountは補助文字の場合に2が、それ以外は1が返るだけなので、インライン展開すればもしかすると違ってくるかも。でもJITコンパイラがそこまでやってそうな気もします。一応PrintCompilationオプションを使ってgetUtf8がJITコンパイルされていることは確認しました。
そこで、インライン化されているかどうか確認してみました。
OpenJDK debug build and JVM Option
インライン化されているか確認する為には、「-XX:+PrintInlining」を付けて実行します。
但し、このオプションはdebug build版のJVMでのみ有効になるということなので、今回はOpenJDK7のコードを取得してdebug buildして使っています。OpenJDKのdebug buildについて以下のエントリを参考にしました。
- LOAD-TIME-VALUE: Clojure 1.3 のパフォーマンスに驚いた話
- メモ:OpenJDK 6 のビルド手順 - 虎塚
- Hiroshi Yamauchi’s Blog: Building OpenJDK
Output
早速、BSONEncoder#_putのUTF-8変換処理を抜き出したコードを「-XX:+PrintInlining」を付けて実行しました。
また、どのメソッドの中でインライン化されたか確認する為に「-XX:+PrintCompilation」も付けています。
1 1065 9 ! net.wrap_trap.example.encode.EncodeUTF8Test::getUtf8 (279 bytes)
2 @ 7 java.lang.String::length (5 bytes)
3 @ 12 java.io.ByteArrayOutputStream::<init> (43 bytes) callee is too large
4 @ 17 java.lang.String::length (5 bytes)
5 s @ 240 java.io.ByteArrayOutputStream::toByteArray (12 bytes)
6 @ 8 java.util.Arrays::copyOf (19 bytes)
7 @ 11 java.lang.Math::min (11 bytes)
8 @ 14 java.lang.System::arraycopy (0 bytes)
9 @ 250 java.io.ByteArrayOutputStream::close (1 bytes)
10 @ 32 java.lang.Character::codePointAt (51 bytes) callee is too large
11 s @ 49 java.io.ByteArrayOutputStream::write (32 bytes)
12 @ 7 java.io.ByteArrayOutputStream::ensureCapacity (16 bytes)
13 @ 12 java.io.ByteArrayOutputStream::grow (50 bytes) callee is too large
14 s @ 77 java.io.ByteArrayOutputStream::write (32 bytes)
15 @ 7 java.io.ByteArrayOutputStream::ensureCapacity (16 bytes)
16 @ 12 java.io.ByteArrayOutputStream::grow (50 bytes) callee is too large
17 s @ 91 java.io.ByteArrayOutputStream::write (32 bytes)
18 @ 7 java.io.ByteArrayOutputStream::ensureCapacity (16 bytes)
19 @ 12 java.io.ByteArrayOutputStream::grow (50 bytes) callee is too large
20 s @ 118 java.io.ByteArrayOutputStream::write (32 bytes)
21 @ 7 java.io.ByteArrayOutputStream::ensureCapacity (16 bytes)
22 @ 12 java.io.ByteArrayOutputStream::grow (50 bytes) callee is too large
23 s @ 135 java.io.ByteArrayOutputStream::write (32 bytes)
24 @ 7 java.io.ByteArrayOutputStream::ensureCapacity (16 bytes)
25 @ 12 java.io.ByteArrayOutputStream::grow (50 bytes) callee is too large
26 s @ 149 java.io.ByteArrayOutputStream::write (32 bytes)
27 @ 7 java.io.ByteArrayOutputStream::ensureCapacity (16 bytes)
28 @ 12 java.io.ByteArrayOutputStream::grow (50 bytes) callee is too large
29 s @ 169 java.io.ByteArrayOutputStream::write (32 bytes)
30 @ 7 java.io.ByteArrayOutputStream::ensureCapacity (16 bytes)
31 @ 12 java.io.ByteArrayOutputStream::grow (50 bytes) callee is too large
32 s @ 186 java.io.ByteArrayOutputStream::write (32 bytes)
33 @ 7 java.io.ByteArrayOutputStream::ensureCapacity (16 bytes)
34 @ 12 java.io.ByteArrayOutputStream::grow (50 bytes) callee is too large
35 s @ 203 java.io.ByteArrayOutputStream::write (32 bytes)
36 @ 7 java.io.ByteArrayOutputStream::ensureCapacity (16 bytes)
37 @ 12 java.io.ByteArrayOutputStream::grow (50 bytes) callee is too large
38 s @ 217 java.io.ByteArrayOutputStream::write (32 bytes)
39 @ 7 java.io.ByteArrayOutputStream::ensureCapacity (16 bytes)
40 @ 12 java.io.ByteArrayOutputStream::grow (50 bytes) callee is too large
41 @ 227 java.lang.Character::charCount (12 bytes)
42 @ 268 java.io.ByteArrayOutputStream::close (1 bytes)
出力がかなり多いので、getUtf8メソッドの箇所のみを抜き出しました。
1行目は「-XX:+PrintCompilation」の出力です。その他はすべて「-XX:+PrintInlining」による出力内容となります。
出力フォーマットの説明を見つけることができなかったので認識違いがあるかもしれませんが、getUtf8メソッドの中でインライン化されたメソッドが「@」に続いて出力されます。そのメソッドからさらに別のメソッドを呼び出してインライン化された場合は、calleeを下に記述しインデントして出力されます。
java.io.ByteArrayOutputStream::growの行の最後に「callee is too large」と表示されている場合は、コードのサイズが大きすぎてインライン化できないこと示しているようです。
最後から2行目に、java.lang.Character::charCountが出力されていることから、前回記述したとおり、このメソッドの呼び出しはJITコンパイルによってgetUtf8メソッド内にインライン化されていました。
MaxInlineSize
インライン化するかどうかを決定するコードサイズは、「-XX:MaxInlineSize」で指定します。デフォルトは35です。
「-XX:MaxInlineSize=100」として再度実行したところ、前回よりインライン化が行われるようになり、出力結果が大きくなった為、getUtf8の一部だけを抜き出しています。
1 s @ 186 java.io.ByteArrayOutputStream::write (32 bytes)
2 @ 7 java.io.ByteArrayOutputStream::ensureCapacity (16 bytes)
3 @ 12 java.io.ByteArrayOutputStream::grow (50 bytes)
4 @ 30 java.lang.OutOfMemoryError::<init> (5 bytes) don't inline Throwable constructors
5 @ 43 java.util.Arrays::copyOf (19 bytes)
6 @ 11 java.lang.Math::min (11 bytes)
7 @ 14 java.lang.System::arraycopy (0 bytes)
8 s @ 203 java.io.ByteArrayOutputStream::write (32 bytes)
9 @ 7 java.io.ByteArrayOutputStream::ensureCapacity (16 bytes)
10 @ 12 java.io.ByteArrayOutputStream::grow (50 bytes)
11 @ 30 java.lang.OutOfMemoryError::<init> (5 bytes) don't inline Throwable constructors
12 @ 43 java.util.Arrays::copyOf (19 bytes)
13 @ 11 java.lang.Math::min (11 bytes)
14 @ 14 java.lang.System::arraycopy (0 bytes)
15 s @ 217 java.io.ByteArrayOutputStream::write (32 bytes)
16 @ 7 java.io.ByteArrayOutputStream::ensureCapacity (16 bytes)
17 @ 12 java.io.ByteArrayOutputStream::grow (50 bytes)
18 @ 30 java.lang.OutOfMemoryError::<init> (5 bytes) don't inline Throwable constructors
19 @ 43 java.util.Arrays::copyOf (19 bytes)
20 @ 11 java.lang.Math::min (11 bytes)
21 @ 14 java.lang.System::arraycopy (0 bytes)
22 @ 227 java.lang.Character::charCount (12 bytes)
23 @ 268 java.io.ByteArrayOutputStream::close (1 bytes)
-XX:MaxInlineSize=100を指定したことにより、java.io.ByteArrayOutputStream::growがインライン化され、行末に「callee is too large」と表示されなくなりました。
さらにjava.io.ByteArrayOutputStream:grow以降のメソッド呼び出しもインライン化されるようになったことが分かります。何故かOutOfMemoryのコンストラクタが表示されていますが…。この行末に出力されているように、Throwableのコンストラクタはインライン化されないようです。
ちなみに、MaxInlineSize=100を指定したところ、処理時間が4倍近く延びました。過剰なインライン化は速度低下を招くということなのかな。
Conclusion
- インライン化されているか確認したい場合は「-XX:+PrintInlining」を付ける
- どのメソッド内でインライン化されたか確認する為に「-XX:+PrintCompilation」も付ける
- 「-XX:+PrintInlining」はdebug build版のJVMでのみ有効に機能する
- メソッドをインライン化するかどうかのサイズの閾値は「-XX:MaxInlineSize」で指定する
- 過剰なインライン化は処理時間を延ばす原因となる?