84
@jyukutyo(じゅくちょ) 阪田 浩一 JITコンパイル はじめの一歩 1

JITコンパイルはじめの一歩

Embed Size (px)

DESCRIPTION

2014/10/27 関ジャバ HotSpot meetingで@jyukutyoが発表した「JITコンパイルはじめの一歩」の資料です。

Citation preview

Page 1: JITコンパイルはじめの一歩

@jyukutyo(じゅくちょ)  阪田  浩一

JITコンパイル  はじめの一歩

1

Page 2: JITコンパイルはじめの一歩

自己紹介

Page 3: JITコンパイルはじめの一歩

3

@jyukutyo

著書 3冊

関西Javaエンジニアの会 (関ジャバ) 発起人・運営

Fight the Future http://jyukutyo.hatenablog.com/

フリュー株式会社 所属 プリ機と連携する

画像SNS「ピクトリンク」 開発・運用に従事. 会員数800万人!

ScalaMatsuri スポンサー企業!

エンジニア歴11年 35歳SI業界での客先常駐 9年 Web系? 2年強

文学部哲学科卒

塾講師アルバイト

阪田 浩一

Page 4: JITコンパイルはじめの一歩

JVM力(ちから)は  今日の発表者の  中で最弱…

Page 5: JITコンパイルはじめの一歩

よろしくお願いします

Page 6: JITコンパイルはじめの一歩

まずは  おさらい

Page 7: JITコンパイルはじめの一歩

JITコンパイルって何?

Page 8: JITコンパイルはじめの一歩

厳密な定義では

Page 9: JITコンパイルはじめの一歩

JITベースの仮想マシンは  実行前に  

全バイトコードを  マシンコードに  変換する(!)

Page 10: JITコンパイルはじめの一歩

ここでは  JITコンパイルを  実行時に  

動的コンパイルする  という意味で進めます

Page 11: JITコンパイルはじめの一歩

Just-­In-­Timeコンパイル  !

メソッドが呼び出されたときに  メソッドのバイトコードをコンパイルして  ネイティブなマシンコードに変換する

Page 12: JITコンパイルはじめの一歩

メリット

Page 13: JITコンパイルはじめの一歩

コンパイル済みの  コードを呼び出すので、  

実行が速い

Page 14: JITコンパイルはじめの一歩

デメリット

Page 15: JITコンパイルはじめの一歩

メソッドをコンパイルする  処理に  

大きなオーバーヘッドが  ある

Page 16: JITコンパイルはじめの一歩

このコンパイルの  タイミングで  

最適化も実施する

Page 17: JITコンパイルはじめの一歩

JVMでは実行時に  プロファイリングし、

Page 18: JITコンパイルはじめの一歩

コンパイルに値する  コードを判断する

Page 19: JITコンパイルはじめの一歩

最適化の種類

§ メソッド呼び出しのインライン化  § 単一ディスパッチ  § 共通部分式の削除  § ループ・アンローリング  § 範囲チェック削除  § デッドコードの削除  § データ・フロー解析

Page 20: JITコンパイルはじめの一歩

例)  メソッドのインライン化

Page 21: JITコンパイルはじめの一歩

呼び出している  メソッドの本体を  呼び出し元に  コピーする

Page 22: JITコンパイルはじめの一歩

イメージしやすい

Page 23: JITコンパイルはじめの一歩

これによって  何がよいのか??

Page 24: JITコンパイルはじめの一歩

小さな処理の場合、  メソッドを呼び出す  コストの方が  高くなる

Page 25: JITコンパイルはじめの一歩

デフォルトでは  35バイト未満の  バイトコードを含む  メソッドが対象となる  ※静的なルール

Page 26: JITコンパイルはじめの一歩

ただ、動的なポリシーも  あるので、  対象は  

アプリケーションごとに  変化する

Page 27: JITコンパイルはじめの一歩

例)  単一ディスパッチ

Page 28: JITコンパイルはじめの一歩

ポリモーフィックな  メソッド呼び出しにおいて、  参照する型が変わらない  

と想定し、

Page 29: JITコンパイルはじめの一歩

仮想メソッド検索を  スルーする  

(オーバーライドが  使用されるかを  チェックしない)

Page 30: JITコンパイルはじめの一歩

脱最適化

Page 31: JITコンパイルはじめの一歩

単一ディスパッチの想定と  違う型のインスタンス  であった場合など

Page 32: JITコンパイルはじめの一歩

コンパイルしたコードを  無効にして  

インタプリタに戻る  (または再コンパイルする)

Page 33: JITコンパイルはじめの一歩

つまり

Page 34: JITコンパイルはじめの一歩

1つのメソッドに対して  脱最適化と  再コンパイルが  繰り返されることも  よくあること

Page 35: JITコンパイルはじめの一歩

コンパイル・モード

Page 36: JITコンパイルはじめの一歩

一度は目にした  -­client  -­server  オプション

Page 37: JITコンパイルはじめの一歩

-­client:C1  コンパイル処理は速い  

生成されるコードは比較的遅い  

-­server:C2  コンパイル処理は遅い  生成されるコードは速い

Page 38: JITコンパイルはじめの一歩

-­client:C1§ ステップ1(インタプリタ)

§ 実行しながらホットなメソッドを検出する

§ ステップ2(ネイティブ)

§ ホットと判断したメソッドをJITコンパイルし、実行する

Page 39: JITコンパイルはじめの一歩

-­server:C2§ ステップ1(インタプリタ)

§ 実行しながらプロファイリングする

§ ステップ2(ネイティブ)

§ ホットと判断したメソッドをステップ1で取得したプロファイリングデータを使ってJITコンパイルし、実行する

§プロファイリングデータを取得するためにインタプリタのフェーズが長い

Page 40: JITコンパイルはじめの一歩

JVMエンジニア  たるもの  

これを使い分けねば  ならん!

Page 41: JITコンパイルはじめの一歩

そんな時代も  ありました…

Page 42: JITコンパイルはじめの一歩

もうこんな  オプション  使いません

Page 43: JITコンパイルはじめの一歩

今は  自動的に  

両方とも使っています

Page 44: JITコンパイルはじめの一歩

階層型コンパイル  JDK7で導入(オプション※で指定が必要)  

JDK6u25までバックポート  JDK8ではデフォルトで有効  

!※-­XX:+TieredCompilation

Page 45: JITコンパイルはじめの一歩

端的に言うと、

Page 46: JITコンパイルはじめの一歩

起動時には  C1を多く使い、  速く起動する

Page 47: JITコンパイルはじめの一歩

正常に動作するように  なればC2をメインに使い、  パフォーマンスを高める

Page 48: JITコンパイルはじめの一歩

もう少し  詳しく見ると、

Page 49: JITコンパイルはじめの一歩

インタプリタではなく  C1の段階で  

プロファイリングする  ようにする

Page 50: JITコンパイルはじめの一歩

コンパイルレベル§ level 0:インタプリタ

§ level 1:C1 フル最適化 § プロファイリングなし

§ level 2:C1 呼び出しとループのプロファイリング

§ level 3:C1 フルプロファイリング

§ level 4:C2

Page 51: JITコンパイルはじめの一歩

コンパイルの  処理方法

Page 52: JITコンパイルはじめの一歩

C1,C2がそれぞれ  キューを持つ

Page 53: JITコンパイルはじめの一歩

C1,C2がそれぞれ  キューを持つ

Page 54: JITコンパイルはじめの一歩

キューの長さに応じて  閾値などを  調整する

Page 55: JITコンパイルはじめの一歩

コードキャッシュ内に  コンパイル後の  

コードが配置されると、

Page 56: JITコンパイルはじめの一歩

インタプリタ・モードから  コンパイルされたコードを  使用するモードに  切り替える

Page 57: JITコンパイルはじめの一歩

これは、ポインタ書き換え  (pointer  swizzing)  と呼ばれる  こともある

Page 58: JITコンパイルはじめの一歩

コンパイルレベルの遷移§ level 0 -> 3 -> 4(理想的)

§ 0 -> 2 -> 3 -> 4(C2のキューが長いとき)

§ 0 -> 3 -> 1(C2コンパイルができないメソッドのとき)??

§ 0 -> 4(C1でコンパイルできない、インタプリタでプロファイリングしてC2する)

Page 59: JITコンパイルはじめの一歩

連続再コンパイル

Page 60: JITコンパイルはじめの一歩

一度コンパイルしたあとも  プロファイリングを  

続ける

Page 61: JITコンパイルはじめの一歩

さらなる最適化の  可能性があれば、  コードを再コンパイル  

する

Page 62: JITコンパイルはじめの一歩

On-­Stack  replacement  (OSR)

Page 63: JITコンパイルはじめの一歩

ループの中で  同一のメソッドを  呼び出している  ような場合

Page 64: JITコンパイルはじめの一歩

ループの最中に  インタプリタから  

コンパイルしたコードを  使うように  切り替える

Page 65: JITコンパイルはじめの一歩

JITコンパイルの  確認方法

Page 66: JITコンパイルはじめの一歩

-­XX:+PrintCompilation  ! 75 1 3 java.lang.Math::min (11 bytes) 75 3 3 java.lang.String::charAt (29 bytes) 76 2 3 java.lang.AbstractStringBuilder::ensureCapacityInternal (16 bytes) 76 4 n 0 java.lang.System::arraycopy (native) (static) 77 5 3 java.lang.Object::<init> (1 bytes) 77 6 3 java.lang.String::hashCode (55 bytes) 77 7 3 java.lang.String::indexOf (70 bytes) 78 8 3 java.lang.String::length (6 bytes) 78 9 3 java.lang.AbstractStringBuilder::append (50 bytes) 78 10 3 java.lang.String::getChars (62 bytes) 78 11 3 java.lang.String::equals (81 bytes)

Page 67: JITコンパイルはじめの一歩

コンパイルされた  メソッドと  

時間、コンパイルレベルが  わかる

Page 68: JITコンパイルはじめの一歩

-­XX:+PrintTieredEvents  !0.047899: [call level=0 [java.lang.Object.<init>()V] @-1 queues=0,0 rate=n/a k=1.00,1.00 total=128,0 mdo=0(0),0(0) max levels=0,0 compilable=c1,c1-osr,c2,c2-osr status=idle]0.048180: [loop level=0 [sun.nio.cs.UTF_8$Decoder.decode([BII[C)I] @20 queues=0,0 rate=n/a k=1.00,1.00 total=29,1024 mdo=0(0),0(0) max levels=0,0 compilable=c1,c1-osr,c2,c2-osr status=idle]0.048348: [loop level=0 [sun.nio.cs.UTF_8$Decoder.decode([BII[C)I] @20 queues=0,0 rate=n/a k=1.00,1.00 total=45,2048 mdo=0(0),0(0) max levels=0,0 compilable=c1,c1-osr,c2,c2-osr status=idle]0.048414: [loop level=0 [java.lang.String.hashCode()I] @24 queues=0,0 rate=n/a k=1.00,1.00 total=73,1024 mdo=0(0),0(0) max levels=0,0 compilable=c1,c1-osr,c2,c2-osr status=idle]0.049419: [call level=0 [java.lang.String.hashCode()I] @-1 queues=0,0 rate=n/a k=1.00,1.00 total=128,1302 mdo=0(0),0(0) max levels=0,0 compilable=c1,c1-osr,c2,c2-osr status=idle]0.049514: [call level=0 [java.lang.Object.<init>()V] @-1 queues=0,0 rate=n/a k=1.00,1.00 total=256,0 mdo=0(0),0(0) max levels=0,0 compilable=c1,c1-osr,c2,c2-osr status=idle]0.049557: [compile level=3 [java.lang.Object.<init>()V] @-1 queues=0,0 rate=n/a k=1.00,1.00]!

Page 69: JITコンパイルはじめの一歩

より詳細に  見れる

Page 70: JITコンパイルはじめの一歩

さらに詳細に  見るには

Page 71: JITコンパイルはじめの一歩

-­XX:+LogCompilation  !

XMLフォーマットで出る  -­XX:+UnlockDiagnosticVMOptions  

を同時に設定する必要あり  !

Page 72: JITコンパイルはじめの一歩

hotspot_pid9999.log  !<nmethod compile_id='2' compiler='C2' entry='0x000000010508d220' size='1504' address='0x000000010508d0d0' relocation_offset='296' insts_offset='336' stub_offset='688' scopes_data_offset='736' scopes_pcs_offset='864' dependencies_offset='1456' handler_table_offset='1464' nul_chk_table_offset='1488' method='java/lang/String indexOf (II)I' bytes='70' count='366' backedge_count='6418' iicount='366' stamp='0.122'/>!

Page 73: JITコンパイルはじめの一歩

もう読めません…

Page 74: JITコンパイルはじめの一歩

ツールを使おう!

Page 75: JITコンパイルはじめの一歩

JIT  Watch  https://github.com/AdoptOpenJDK/jitwatch/

Page 76: JITコンパイルはじめの一歩

セットアップは  cloneして  mvn  package  用意されている  launchUI.sh(.bat)  を実行するだけ

Page 77: JITコンパイルはじめの一歩

超簡単!

Page 78: JITコンパイルはじめの一歩

ログを解析するときは

Page 79: JITコンパイルはじめの一歩

-­XX:+TraceClassLoadingでクラスロード時の  情報も出す

Page 80: JITコンパイルはじめの一歩

さらに

Page 81: JITコンパイルはじめの一歩

HotSpotの  内部オプション  

-­XX:+PrintAssembly  をつければ  

アセンブリのコードを  見れる

Page 82: JITコンパイルはじめの一歩

※HotSpot  disassembleer  (HSDIS)  が別途必要  

https://kenai.com/projects/base-­hsdis  JDK  8ならファイルをJAVA_HOME/jre/lib/server/へ  

コピーする

Page 83: JITコンパイルはじめの一歩

デモ

Page 84: JITコンパイルはじめの一歩

ご清聴 ありがとう ございました!