コンパイラ 2012 年 11 月 19 日
DESCRIPTION
コンパイラ 2012 年 11 月 19 日. 酒居敬一@A468 ( [email protected] ) http://www.info.kochi-tech.ac.jp/k1sakai/Lecture/COMP/2012/index.html. 動的コンパイラ. コンパイル処理という負荷が実行時に発生 コンパイラがメモリを消費する(空間的負荷) 一時記憶・二次記憶が貴重な環境では大問題 動的コンパイルに費やす時間(時間的負荷) 起動時に集中して動的コンパイル コンパイルが終わるまで、アプリケーションが応答しない - PowerPoint PPT PresentationTRANSCRIPT
コンパイラ2012 年 11 月 19 日
酒居敬一@A468 ([email protected])
http://www.info.kochi-tech.ac.jp/k1sakai/Lecture/COMP/2012/index.html
1
動的コンパイラ コンパイル処理という負荷が実行時に発生
コンパイラがメモリを消費する(空間的負荷) 一時記憶・二次記憶が貴重な環境では大問題
動的コンパイルに費やす時間(時間的負荷) 起動時に集中して動的コンパイル
コンパイルが終わるまで、アプリケーションが応答しない インタプリタ実行の結果により、動的コンパイル
初期化ルーチンは、たいてい1回しか実行されないので、コンパイル無用
実行プロファイルに基づく最適化コンパイルができる インタプリタ実行中にプロファイルを集める 条件分岐の頻度を反映したコードを出力する
2
on stack replacement 実行頻度が高い、とは?
メソッドの… 呼んで処理して帰ってくるメソッドならば対応しやすい メソッドを呼んだ回数を数えて動的コンパイルするかどうか
決める メソッド呼び出し時にインタプリタ実行か Native 実行かを
選択可能 ブロックの…
たとえば、無限ループを含むメソッドは呼んだらそれっきり 呼んだっきりでは、実行の途中で切り替えるしか方法がない
途中で Native 実行に切り替えるには? 動的コンパイラでコンパイルする on stack replacement により動作を引き継ぐ
3
4
1. public class Example10 {2. public volatile Executable _job;3. synchronized void wait_and_go() throws InterruptedException {4. while(true){5. this.wait();6. this._job.execute();7. }8. }9. …10.}11.public class Executable{12. public static Executable escape_path;13. public void execute(){14. if(unluckey()){15. try{16. Class klass = System.loadClass("Executable2");17. escape_path = (Executable)(klass.newInstance());18. }catch(Throwable t){}19. }20. }21. …22.}23.public class Executable2 extends Executable{24. public void execute(){25. …26. }27.}
on stack replacement の手順 インタプリタは実行頻度が高いメソッドを見つけコン
パイル ループ回数が多いループを含むメソッドも実行頻度が高い
とする 実行を引き継ぐ
変数などの格納位置を修正する レジスタ割り当ての変化 変数のスタック - レジスタ間の移動 レジスタの退避・復帰
実行中のバイトコードに対応する Native コードに分岐 コンパイル時に対応表を用意する
制御フロー解析と最適化の結果から生成すればよいだけ
5
wait_and_go() のバイトコード表現
6
1. Method void wait_and_go()2. 0 aload_03. 1 invokevirtual #2 <Method void wait()>4. 4 aload_05. 5 getfield #3 <Field Executable _job>6. 8 invokevirtual (args 1) #4 <VirtualMethod void execute()>7. 13 goto 0
wait_and_go() の Native コード例
7
1. addil L'0x2000, %sp, %r1 ; %r1 <- %sp( スタックポインタ ) + 0x20002. stw %r0, 0(%r1) ; %sp + 0x2000 が参照可能か検査 (stack banging)3. copy %sp, %fp ; %fp( フレームポインタ ) <- %sp4. ldo 0xC0(%sp), %sp ; %sp += スタックフレームサイズ5. stw %fp, -0x4(%sp) ; フレームの開放に備え %fp (= 旧 %sp) を退避6. stw %rp, -0x8(%sp) ; %rp( 戻り番地 ) をフレームに退避7. stw r3, 0(%fp) ; 呼び出し先保存レジスタ (%r3) の退避8. copy %r26, %r3 ; %r3 <- this (%r26 には 0 番引数 this が入っている )9. ENTER_SYNC(%r3, 0x4(%fp)) ; [ マクロ ]%r3(this) の排他制御権を確保10.Loop:11. call WAIT ; wait() の呼び出し12. copy %r3, %r26 ; [ 遅延スロット ] %r26(0 番引数 ) <- this13. ldw,o 8(%r3), %r26 ; %r26(0 番引数 ) <- this._job14. call EXECUTE ; execute() の呼出し15. ldw 0(%r26), %r1 ; [ 遅延スロット ] 暗黙の null 検査 , _job==null?16. b Loop ; ラベル Loop にジャンプ17. nop ; [ 遅延スロット ] 何もしない(することがない)18. …
レジスタ群
8
実行時スタック
コンパイル済みコード実行時
インタプリタ実行時
オペランドスタックポインタ確保済み排他制御権スタックポインタ
…%r32
%r6%r5%r4%r3
%r0
局所変数領域への参照バイトコードプログラムカウンタ
…
%r6 退避値%r5 退避値
…%r32
%r6%r5%r4%r3
%r0
this%r4 退避値
…
%sp→
%fp→
this の排他制御権%r6 退避値%r5 退避値%r4 退避値%r3 退避値旧々 %sp戻り番地
this
戻り番地旧 %sp
…
%sp→
%fp→this の排他制御権
%r3 退避値旧々 %sp戻り番地
this
戻り番地旧 %sp
…
確保済み排他制御権スタック
確保済み排他制御権格納領域
呼び出し先保存レジスタ退避領域
呼び出し先保存レジスタ退避領域
局所変数格納領域
局所変数格納領域
wait_and_go()のインタプリタ用スタックフレーム
wait_and_go() のコンパイル済みコード用スタックフレーム
on stack replacement脱最適化
on stack replacement と脱最適化
動的文脈を利用した最適化 「 call EXECUTE 」のところは直接呼出し
本来は(メモリを介した)間接呼び出しである 「 getfield #3 <Field Executable _job> 」に対応
実行中に矛盾が生じた場合、脱最適化を行う _job フィールドが別のインスタンスを指す場合に矛盾を生じる
動的文脈を利用して、脱仮想化という最適化をしている ループ内では _job フィールドが不変であると仮定している
この仮定が崩れる場合は、本来のバイトコード実行に戻す、としている
本来では、オブジェクト変数からインスタンスの参照、インスタンスから該当メソッドのエントリーポイントの獲得および分岐、という一連の処理を、直接分岐命令に置き換えている
9
Java の仮想呼出しの実装1. 実行時の型、つまりクラスを表すデータ構造を取り
出す2. 該当のメソッドのエントリーポイントをレジスタに
代入3. レジスタを介した間接ジャンプ(遅延分岐)
10
1. ldw 4(%r26), %r2 ; %r2 <- インスタンス (%r26) のクラスを表すデータ構造2. ldw METHOD_ID(%r2), %r2 ; %r2 <- 呼出し対象のメソッドの番地3. bve,l %r2 ; %r2 が指示する番地にジャンプ ( 間接ジャンプ )4. nop ; [ 遅延スロット ] 何もしない ( することがない )
IF ID WBEX
IF ID WBEX
IF ID WBEX
遅延分岐命令
遅延スロットの命令
分岐先の命令
X+0 X+1 Y+0 Y+ 1PC: Y+ 2
分岐命令が EX ステージに入る前は分岐前 PC を基準に Instruction Fetch
11
ヘッダクラス
Executable クラスのインスタンス
ヘッダクラス
Executable 2クラスのインスタンス
Executable クラスを表すデータ構造
Executable 2クラスを表すデータ構造
Executable クラスが定義する execute()
のオブジェクトコード
…
java.lang.ObjectExecutable
…
固定長部分
execute()のメソッド番号
ディスパッチ表
親クラスクラス名
Executable 2クラスが定義する execute()
のオブジェクトコード
…
ExecutableExecutable2
…
固定長部分
execute()のメソッド番号
ディスパッチ表
親クラスクラス名
フィールド _job
フィールド _job
動的文脈と静的文脈 静的文脈
プログラムに書いてあるとおりに解釈した文脈 脱仮想化のために、変数の到達定義連鎖を利用する
動的文脈静的文脈の他、実行時の情報も加味して解釈した文脈 ユーザーからの入力 コマンドライン引数 実行環境 (OS ・ CPU) たとえば、シングルトンパターン
if(!initialized){ 初期化 ; initialized = true;}
12
静的文脈を利用して脱仮想化できる仮想呼出し 2 行目の e.execute() を参照するとき、そこに到達する
定義は 1 行目にあり、それが唯一である。
本来はインスタンスから実行時クラスの情報を取り出し、そこから該当のメソッドを選択し間接的に呼ぶ。しかし、到達する定義から明らかなように、実行時クラスは Executable と静的に読み取れることから、2行目を直接 Executable の execute() を呼ぶようにする。
13
1. Executable e = new Executable();2. e.execute();
脱最適化 一方で、 152 ページのリスト 10.1 の _job では、静的
な脱仮想化はできない。 静的コンパイルの結果、 _job を参照する行へ到達する
定義が唯一ではないときには、静的に脱仮想化できない。特に、 volatile で広いスコープを持つ場合は、参照するごとに値が変わっている、と推定するしかない。
ただし、実行時に値が変わらないようであれば脱仮想化してもいい、という意味でもある。そこで、 動的文脈を監視し、仮定が成立しているか調べる機能 仮定が不成立のときに、最適化を解除する機能(脱最適化)
を持たせれば、 _job に関するような文脈でも、動的な最適化ができる。
14
最適化の解除 動的文脈の監視
Native コードが使えなくなる条件を動的コンパイラが付与し、実行時にその条件を監視させる。
最適化の解除 条件が不成立となった場合
Native コードを上書きして再利用する 新しくコンパイルしなおすか、バイトコードのインタプリタ実行に戻
る 脱最適化するスレッド以外のスレッドを停止 全てのスレッドのスタックフレームを検査 矛盾のある破棄対象コードへ戻ることがないように戻り番地を書き換える
書き換え先は脱最適化ハンドラ スレッドの実行を再開 (たとえば)脱最適化ハンドラが呼ばれるたび、インタプリタ実行に戻る
15
16 脱最適化準備段階
脱最適化前
wait_and_go:addil L'0x2000, %sp, %sp, %ri
Loop:call WAITcopy %r3, %r26ldw,o 8(%r3), %r26call EXECUTEldw 0(%r26), %r1b Loopnop
Class::newInstance()のスタックフレーム
戻り番地呼出し元の %sp
戻り番地呼出し元の %sp
戻り番地呼出し元の %sp
戻り番地呼出し元の %sp
戻り番地呼出し元の %sp
戻り番地呼出し元の %sp
Executable::execute()のスタックフレーム
Executable10::wait_and_go()のスタックフレーム
Class::newInstance()のスタックフレーム
Executable::execute()のスタックフレーム
Executable10::wait_and_go()のスタックフレーム
wait_and_go:addil L'0x2000, %sp, %sp, %ri
Loop:call WAITcopy %r3, %r26ldw,o 8(%r3), %r26call EXECUTEldw 0(%r26), %r1b Loopnop
戻り番地退避先
脱最適化ハンドラ:…
Q 末試験 日時
2012年11月22日2時限目 場所
A 106(いつもの講義室) 時間
10 時 40 分から 12 時 10 分までの1時間 30 分開始後 30 分過ぎたら退室可開始後 30 分までは入室可(遅刻限度)
持ち込んでいいもの紙の資料、紙の書籍 (紙媒体であればいい)
17