パート3 jitwatchによるjava jitコンパイルの調査...hotspot vmによる最...
TRANSCRIPT
![Page 1: パート3 JITWatchによるJava JITコンパイルの調査...HotSpot VMによる最 適化判断の根拠とな るプロファイリング情 報が少なくなるから です。mvn](https://reader033.vdocuments.pub/reader033/viewer/2022041815/5e5a49cce2efb7132e08eed9/html5/thumbnails/1.jpg)
ORACLE.COM/JAVAMAGAZINE ////////////////////////////// MARCH/APRIL 2015
JAVA TECH
16
COMMUNITY
JAVA IN ACTION
ABOUT US
blog
//java architect /
オラクルのJava HotSpot VMには各種の動的コンパイラが
搭載されています。これらの動的コンパイラは、実行中のプログラムから統計情報を収集し、その統計情報を使用してパフォーマンスを最適化します。その最適化のために、すでにインタプリタされたバイトコードの一部を選択し、Just-In-Time(JIT)コンパイル技術を用いて、より高速に実行できるネイティブ・コードへと置き換えます。JITWatchは無償のオープンソース・ツールで、Java HotSpot VMが生成したコンパイル時の複雑なログ・ファイル出力を分析し、Java HotSpot VMにより決定された最適化の内容を、開発者が理解しやすいように視覚化します。そのJITWatchをAdopt OpenJDKプロジェクトで開発したのが筆者の1人、Chris Newlandです。JITWatchはGitHubからダウンロードできます。最新情報については、ChrisのTwitter(@chriswhocodes)をフォローして確認してください。
この全3回シリーズのパート2では、JITWatchが必要とする情報をJava HotSpot VMで生成する方法について説明しました。本記事では、Java HotSpot VMのさらに高度な機能をいくつか紹介し、コードの変更やJava HotSpot VMの振る舞いに関する設定の影響を、JITWatch Sandboxを使用してテストする方法について説明します。
JITWatchについての再確認GitHubからJITWatchのバイナリをダウンロードするか、または以下のコマンドでソース・コードからJITWatchのバイナリをビルドします。
この後、./launchUI.sh(Linux、Mac OS)または./launchUI.bat(Microsoft Windows)を実行して、JITWatchを起動します。準備段階に関する詳細な説明は、wikiを参照してください。Java HotSpot VMの高度な機能
を見ていく際に、逆アセンブル・ライブラリ(hsdis)をJavaランタイム環境(JRE)にインストールしておくと便利です。JITコンパイラが生成するネイティブ・コードを逆アセンブルして参照できるようになります。hsdisのビルド方法についてはこちらを参照してください。
Sandbox入門S a n d b o x と はJITWatchの機能の1つで、コードの編集やその後のコンパイル、実行、そしてJava HotSpot VM JITログの分析を、すべてJITWatchアプリケーションから実行できるようにするものです。Sandboxによって、開発者はプログラムの実行時にJava HotSpot VMの「内部で」何が起きているか
を理解し、ソース・コードの少量の変更やJava HotSpot VMスイッチの影響を調査しやすくなります。注意点として、Sandbox内部で各アルゴリズムを単独でテストしたときに、Java HotSpot VMコンパイラによる最適化の判断が、アプ
リケーション全体の実行時の判断とは異なるものになる場合があります。単独でテストした場合は、Java HotSpot VMによる最適化判断の根拠となるプロファイリング情報が少なくなるからです。
mvn clean install
ソース・コードの少量の変更やJava HotSpot VMスイッチの影響を確認する
パート3
JITWatchによるJava JITコンパイルの調査
CHRIS NEWLAND AND BEN EVANS
ログの分析
JITWatchは無償のオープンソース・ツールで、Java HotSpot VMが生成したコンパイル時の複雑なログ・ファイル出力を分析し、Java HotSpot VMにより決定された最適化の内容を、開発者が理解しやすいように視覚化します。
CHRIS NEWLAND氏の写真:DAVID NEWLAND、BEN EVANS氏の写真:JOHN BLYTHE
Java andPerformance
Chris Newland (@chriswhocodes):1999年よりJavaでの開発を手がけるシニア・ソフトウェア・エンジニア。仕事で金融取引プラットフォームを構築し、趣味でloTの研究を行う。 Ben Evans (@kittylyst):jClarityの創業者/技術フェローでありLondon Java Community主催者。Java SE/EE Executive Committeeのメンバーでもある。
![Page 2: パート3 JITWatchによるJava JITコンパイルの調査...HotSpot VMによる最 適化判断の根拠とな るプロファイリング情 報が少なくなるから です。mvn](https://reader033.vdocuments.pub/reader033/viewer/2022041815/5e5a49cce2efb7132e08eed9/html5/thumbnails/2.jpg)
ORACLE.COM/JAVAMAGAZINE ////////////////////////////// MARCH/APRIL 2015
JAVA TECH
17
COMMUNITY
JAVA IN ACTION
ABOUT US
blog
//java architect /
JITWatchの起動後、メイン・ウィンドウの左上にある「Sandbox」ボタンをクリックすると、図1のようなSandboxウィンドウが開きます。Sandboxは、Javaソース・コードのほか、ScalaとGroovyもサポートしています。これらの言語では、Java仮想マシン(JVM)で実行されるバイトコードへのコンパイルが行われるためです。Kotlin、Clojure、JRuby、JavaScript(Nashornを利用)といった他のJVM言語は今後サポートされる予定です。JITWatchはコードのコンパイルと実行のために、内部的に java . lang .ProcessBuilderを利用します。
Java HotSpot VMスイッチを 用いた実験まずは、JITコンパイルの制御に利用できるJava HotSpot VMスイッチについて確認します。「Configure Sandbox」ボタンをクリックして、図2のSandbox Configuration
ウィンドウを開きます。Sandboxで使用する各VM言語は、ホーム・ディレクトリを指定してセットアップします。次に、「Show Disassembly」を選択し、JITによりコンパイルされたネイティブ・コードから、人間が判読可能なアセンブリ言語を生成するように、Java HotSpot VMを設定します。この機能を使用するためにはhsdisバイナリが必要です。Tiered Compilationオプションのデフォルト設定を変更できます。このオプションは、コードが最適化されるタイミングを制御するものです。Tiered Compilationの最適化は、最初にC1クライアントのコンパイラを用いて即座に実施され、その後、実行時統計の収集が進んだ時点で、より高度なC2サーバーのコンパイラを用いて再度実施されます。このTiered Compilationオプションは、Java SE 7ではデフォルトで無効、Java SE 8ではデフォルトで有効です。Java HotSpot VMは、OOP(Ordinary
Object Pointer)と呼ばれる、オブジェクトへのポインタを管理します。OOPは通常、ネイティブ・マシンのポインタと同じサイズになります。そのため、64ビット・システムで必要なヒープ領域は32ビット・システムよりも大きくなりますが、Java HotSpot VMでは、64ビット・ポインタを64ビット・ベースからの32ビットのオフセットとして表すことで、ヒープ領域を節約できます。この機能は、圧縮OOPと呼ばれます。圧縮OOPオプションを無効に
すれば、圧縮OOPのサポートに必要となるポインタ演算コードが不要になるため、逆アセンブルされたネイティブ・コードの調査が容易になります。本番環境でこの設定を変更する場合は、その前に影響範囲を把握するようにしてください。詳細は、『Compressed Oops』を参照してください。
図2
図1
![Page 3: パート3 JITWatchによるJava JITコンパイルの調査...HotSpot VMによる最 適化判断の根拠とな るプロファイリング情 報が少なくなるから です。mvn](https://reader033.vdocuments.pub/reader033/viewer/2022041815/5e5a49cce2efb7132e08eed9/html5/thumbnails/3.jpg)
ORACLE.COM/JAVAMAGAZINE ////////////////////////////// MARCH/APRIL 2015
JAVA TECH
18
COMMUNITY
JAVA IN ACTION
ABOUT US
blog
//java architect / F r e q I n l i n e S i z eフィールドとMaxInlineSizeフィールドには、Java HotSpot VMによってインライン化されるバイトコードの上限サイズをしきい値として指定します(FreqInlineSizeがホット・メソッド、MaxInlineSizeが小さいメソッドに対応)。インライン化を完全に無効にすることもできます。無効化によってパフォーマンスがどの程度低下するかを測定しても面白いでしょう。Compile Thresholdフィールドには、C2サーバーのコンパイラの起動条件としてメソッド呼出し回数を設定します。Extra VM switchesフィールドには、その他のチューニング・パラメータを指定できます。オプションにマウス・ポインタを重ねると、Java HotSpot VMに渡されるスイッチの正確な情報がツールチップとして表示されます。
JITメソッドのインライン化次に、メソッドのインライン化と呼ばれるJITの技法がJava HotSpot VMでどのように利用されているかについて、Sandboxを利用して学習します。メソッドのインライン化とは、メソッド呼出しのオーバーヘッドを除去するために、ターゲット・メソッドの呼出し部分をメソッド本体に置き換えることです。ループのアンロールなど、さらに踏み込んだ最適化を実施する前に、関連のあるコード同士を近づけることができるため、JITコンパイラにとってこの初期の手順は重要です。Java HotSpot VMでは、具体的な型は実行時に初めて判明するインタフェース
型でのメソッド呼出し時にコードをインライン化できます。そのために、以下のように、プログラムの実行中に使用される実装の数をカウントします。
■ 呼出しが「単相形」(実装が1つ)と観察される場合、そのコードは容易にインライン化可能である
■ 呼出しが「二相形」(実装が2つ)と観察される場合、Java HotSpot VMは両方の実装をインライン化し、単純な条件分岐によっていずれかを選択できるようにする
■ 「多相形」(実装が3つ以上)の振る舞いが観察される場合はルックアップ・テーブルが必要になり、Java HotSpot VMはこのようなメソッドの実装をインライン化できない観察される実装の数が増え、いったん下した判断を再考しなければならない状況に備え、Java HotSpot VMは、最適化されたコードに対してアンコモン・トラップを付加します。以降は、Coinというインタフェースのサ
ンプルを使用します。このインタフェースには、deposit()というメソッドが1つ宣言されており、インタフェースの実装としてNickel、Dime、Quarterの3つが存在します。実行時に確認される実装が2つ(二相形ディスパッチ)の場合と3つ(多相形ディスパッチ)の場合で、振る舞いが変化することを観察できます。Sandboxで、PolymorphismTest.javaと
いうソース・ファイルを開きます(このファイルはJITWatchのサンプルに含まれています)。図3のリストを見てください。24行目にあるmaxImplementations変数の設
定によって、使用される実装の数を変更できます。Sandboxウィンドウの「Run」ボタンをクリックしてプログラムを実行します。Java HotSpot VMログ・ファイルの分析後すぐに、図4のJITWatch TriViewウィンドウが開きます。2つのCo in実装を使用した場合、
Bytecodeペインのオフセット93にあるinvokeinterface命令にマウス・ポインタを重ねると、メソッドが正常にインライン化されたことを示すツールチップが表示されます(図4)。
図3
![Page 4: パート3 JITWatchによるJava JITコンパイルの調査...HotSpot VMによる最 適化判断の根拠とな るプロファイリング情 報が少なくなるから です。mvn](https://reader033.vdocuments.pub/reader033/viewer/2022041815/5e5a49cce2efb7132e08eed9/html5/thumbnails/4.jpg)
ORACLE.COM/JAVAMAGAZINE ////////////////////////////// MARCH/APRIL 2015
JAVA TECH
19
COMMUNITY
JAVA IN ACTION
ABOUT US
blog
//java architect /
A s s e m b l y ペ イ ン で は 、Polymorphism$Nickelインナー・クラスのdeposit()メソッドからインライン化されたネイティブ・コードを確認できます。このコードでは、0x000000010249c693番地でadd $0x5,%r11dというアセンブリ命令が使われ、ソース・コードの7行目に
あるmoneyBox静的変数に5が加算されます。A s s e m b l y ペ イ ン 下 方 の0x000000010249c6dd番地ではadd $0xa,%r11dというアセンブリ命令が使われています。ソース・コードの9行目に対応し、Polymorphism$DimeのmoneyBox
静的変数に10を加算する部分がインライン化された結果です。最適化がどのように実行されたか正確に知るため、TriView画面の左上の「JIT Journal」ボタンをクリックします。この二相形インライン化のアンコモン・トラップは、図5の赤い点で示した場所
にあります。3つのCoin実装のすべてが利用される場合、deposit()メソッドの呼出しをインライン化することはできず、図6のように、バイトコード・オフセット93ではインライン化に関するツールチップは表示されません。
図4
![Page 5: パート3 JITWatchによるJava JITコンパイルの調査...HotSpot VMによる最 適化判断の根拠とな るプロファイリング情 報が少なくなるから です。mvn](https://reader033.vdocuments.pub/reader033/viewer/2022041815/5e5a49cce2efb7132e08eed9/html5/thumbnails/5.jpg)
ORACLE.COM/JAVAMAGAZINE ////////////////////////////// MARCH/APRIL 2015
JAVA TECH
20
COMMUNITY
JAVA IN ACTION
ABOUT US
blog
//java architect / また、図4のような、deposit()をインライン化したアセンブリ・コードも存在せず、invokeinter faceバイトコードのvirtual_callは除去されません。さらに、ネイティブ・コードのサイズ増加につながるインライン化が行われないため、ネイティブ・コードのサイズ(504バイト)は、二相形ディスパッチの場合よりも小さくなります。
本記事ではこれ以上深くは説明しませんが、さらに詳細に学習したい場合は、Sandboxに付属するサンプルを実行し、Java HotSpot VMで実行可能な最適化を精査することをお勧めします。
図5
図6
![Page 6: パート3 JITWatchによるJava JITコンパイルの調査...HotSpot VMによる最 適化判断の根拠とな るプロファイリング情 報が少なくなるから です。mvn](https://reader033.vdocuments.pub/reader033/viewer/2022041815/5e5a49cce2efb7132e08eed9/html5/thumbnails/6.jpg)
ORACLE.COM/JAVAMAGAZINE ////////////////////////////// MARCH/APRIL 2015
JAVA TECH
21
COMMUNITY
JAVA IN ACTION
ABOUT US
blog
//java architect / JarScanツールJITWatchのダウンロード・ファイルには、JarScanというツールが含まれています。このツールは、JARファイルのリストをスキャンして、見つかったすべてのメソッドおよびコンストラクタのバイトコードのサイズを計算します。このツールの目的は、バイトコードのバイト・サイズが、Java HotSpot VMの「ホット」メソッドのインライン化に関するしきい値を超えているメソッドをハイライト表示することです。この情報を参考にベンチマークの場所を決め、コードをより小さな単位に分割すればプログラムのパフォーマンスを向上させられるかという問題を調べることができます。JITコンパイラは、メソッド呼出しの頻度や、(ループ上などでの)戻りの分岐の発生回数など、一連の経験則を用いてメソッドの「ホット度」を判断します。インライン化できるコードには各種の制限(呼び出されるメソッドのバイトコード・サイズに関する制限など)があります。この振る舞いは、Java HotSpot VMスイッチの-XX:FreqInlineSize=nを使用してチューニングできます(デフォルトの「ホット」バイトコード・サイズのしきい値は64ビットLinuxで325バイト)。JarScanツールの詳細はwikiを参照してください。メソッドのバイトコード・サイズがFreqInlineSizeを上回っているからといって、そのサイズ超過がパフォーマンス・ボトルネックを示しているとは言い切れません。そのコードが、ホット・コードの制限に達するほど呼び出されない可能性も
あります(JarScanは静的分析ツールであり、メソッドの実際の使用方法については把握していません)。それでは、JarScanツールを使用してrt.jarファイルを調査します。このファイルには、Javaの最新バージョンのJavaコア・ライブラリ(java.lang.*、java.util.*など)が含まれます。リスト1のコマンドを実行します。コマンドの実行には数分かかることがあります。JARファイル内のクラス・ファイルの各メソッドを、API形式のjavapプログラム(com.sun.tools.javap.JavapTask.)で逆アセンブルするためです。64ビットLinuxのJava SE Development Kit 8, Update 31(JDK 8u31)の場合、3,605個のメソッドがFreqInlineSizeのしきい値を上回っていますが、その多くについては想定内です。たとえば、java.util.GregorianCalendar.computeFields()には、バイトコードのサイズ(1,571バイト)に見合う大量の処理が含まれています。ただし、一部の結果については疑問もあります。java.lang.String.toUpperCase()やjava.lang.String.toLowerCase()など、簡潔なループで記述できそうなコア・ライブラリ・メソッドが含まれているからです。これらのメソッドのバイトコードは、439バイトにもなります。Javadocの説明によると、すべての言語のキャラクタ・セットをサポートする必要があるためで、「大文字と小文字は必ずしも1対1の文字として対応せず、返されるStringの長さは元のStringの長さと異なる場合がある」とあります。したがって、これらのメソッドには、内部的な文字配列を すべてのリストのテキストをダウンロード
./jarScan.sh /home/chris/jdk1.8.0_31/jre/lib/rt.jar > methods.csv
LISTING 1 LISTING 2a LISTING 2b LISTING 3
![Page 7: パート3 JITWatchによるJava JITコンパイルの調査...HotSpot VMによる最 適化判断の根拠とな るプロファイリング情 報が少なくなるから です。mvn](https://reader033.vdocuments.pub/reader033/viewer/2022041815/5e5a49cce2efb7132e08eed9/html5/thumbnails/7.jpg)
ORACLE.COM/JAVAMAGAZINE ////////////////////////////// MARCH/APRIL 2015
JAVA TECH
22
COMMUNITY
JAVA IN ACTION
ABOUT US
blog
//java architect / チェックして、必要に応じてサイズの変更を行うコードが含まれています。ここで考え付くのは、アプリケーション・ドメインでコア・ライブラリの柔軟性は求められておらず、しかもパフォーマンスが非常に重視される場合、柔軟性を落としたバージョンのコア・ライブラリ・
メソッドを作成すればパフォーマンスが大幅に向上する可能性があるのではないか、というアイデアです。そのテストを行うため、Stringオブジェクトを大文字に変換するメソッドを記述しました。このメソッドは、ASCII文字のa~zにのみ作用します。java.lang.String.
toUpperCaseとパフォーマンスを比較するため、OpenJDK JMHベンチマーキング・フレームワークを使用します。このベンチマークをSandboxからではなくコマンドラインで実行し、生成されたJava HotSpot VMログ・ファイルをJITWatchに読み込んで分析します。
このベンチマークのコードをリスト2aと2bに示します。ベンチマークの結果、すべてのキャラクタ・セットをサポートすると、その代償は大きいことが分かりました。1秒間に実行された操作の数について見ると、カスタム・バージョンはコア・ライブラリ・バージョンの4.5倍を上回りました(リスト3)。JMHのログ・ファイルの分析によれば、toUpperCaseASCIIメソッドにより生成されるバイトコードのサイズは69バイトで、このメソッドによるメソッド呼出しはすべて正常にインライン化されました(図7の緑色でハイライトされた箇所を参照)。
まとめJava HotSpot VMにより実行できる最適化について紹介した本シリーズはこれで終了です。wikiの「Per formance Techniques」セクションを参照し、JITWatch Sandboxに付属するサンプルを色 と々試してみてください。 ご不明な点やバグ報告がある場合は、GitHub経由でChrisまでご連絡ください。</article>
図7
LEARN MORE• GitHub repository for JITWatch
Java andPerformance
MORE ON TOPIC: