Diese Präsentation wurde erfolgreich gemeldet.
Wir verwenden Ihre LinkedIn Profilangaben und Informationen zu Ihren Aktivitäten, um Anzeigen zu personalisieren und Ihnen relevantere Inhalte anzuzeigen. Sie können Ihre Anzeigeneinstellungen jederzeit ändern.
© 2015 IBM Corporation
高知工科大学 情報学群 コンパイラ
コンパイラにおける最適化技術
2015年11月12日
石崎 一明 kiszk@acm.org
(資料作成協力 井上 拓、緒方 一則、仲池 卓也)
日本アイビーエム...
© 2015 IBM Corporation
IBM Research - Tokyo
自己紹介
石崎 一明(いしざき かずあき) http://ibm.co/kiszk
1992年3月 早稲田大学理工学研究科修士課程電気工学専攻を修了。
19...
© 2015 IBM Corporation
IBM Research - Tokyo
今日の講義内容
 講義内容
– 基本的な最適化
• 実行前コンパイルでも、実行時コンパイルでも使います
– 実行時情報を使った最適化
– インタプリタが存...
© 2015 IBM Corporation
IBM Research - Tokyo
コンパイラの構成
中間コード生成
プラットフォーム共通最適化
コード生成
プラットフォーム固有最適化
バイナリ生成
コントロールフローグラフ
(CFG)
プ...
基本的な最適化
© 2015 IBM Corporation
IBM Research - Tokyo
最適化(Optimization)とは?
 目的
– 実行時間を短縮する
• 一台のコンピュータを使って この後はこの最適化について
• 複数台のコンピュ...
© 2015 IBM Corporation
IBM Research - Tokyo
最適化の方法
 どこを
– ソースコードで見える部分
– ソースコードで見えない部分
 どの範囲で?
– 基本ブロック内
• 基本ブロック(Basic ...
命令の実行時間を減らす
© 2015 IBM Corporation
IBM Research - Tokyo
命令の実行回数を減らす最適化の例
 コンパイル時に前もって計算
– 定数の畳込み
– 定数伝播
 冗長な命令の除去
– 複写伝播
– 不要命令除去
 ...
© 2015 IBM Corporation
IBM Research - Tokyo
定数の畳込み(constant folding)
 定数計算をコンパイル時に行う。
– 言語によっては、決められたデータの精度や変数がとることができる値の...
© 2015 IBM Corporation
IBM Research - Tokyo
定数伝播(constant propagation)
 定数値を、後続の文に伝搬させる。
11
x = 6 
…      // この間にxの定義がない
...
© 2015 IBM Corporation
IBM Research - Tokyo
複写伝播(copy propagation)
 単純な代入命令を、右辺のオペランドに伝搬させる。
12
x = a  // xの定義はここだけ
…    ...
© 2015 IBM Corporation
IBM Research - Tokyo
不要命令除去(dead code elimination)
 メソッド中で使われない値を計算する命令を削除する。
 実行されない文を削除する。
13
…...
© 2015 IBM Corporation
IBM Research - Tokyo
共通部分式の削除(common subexpression elimination)
 一度計算された式の値を再利用する。
14
x = a*b 
… /...
© 2015 IBM Corporation
IBM Research - Tokyo
部分冗長性の削除(partial redundancy elimination)
 一度計算された式の値を再利用するために、式が存在しなかった実行経路へ、...
© 2015 IBM Corporation
IBM Research - Tokyo
コード上昇(code hoisting)
 式を実行順で前方へ移動する。
 片方の経路でしか実行されない式でも移動可能
– 副作用を持つ式の移動に注意
...
© 2015 IBM Corporation
IBM Research - Tokyo
ループ内不変値移動(loop invariant code motion)
 ループ内で定数の値をループ外へ移動する。
– 副作用を持つ式の移動に注意
1...
© 2015 IBM Corporation
IBM Research - Tokyo
メソッド展開(method inlining)
 メソッド呼び出しの場所に、メソッド本体を展開する。
– 最適化の適用範囲が大きくなる。
– ソースコード...
© 2015 IBM Corporation
IBM Research - Tokyo
末尾複製(tail duplication, splitting)
 制御の合流部分以降を複製して、専用コードを作る。
– 変数の性質が一意に決定する、の...
速い命令を使う
© 2015 IBM Corporation
IBM Research - Tokyo
速い命令を使う最適化の例
 命令強度軽減
 スカラ置き換え
 レジスタ割付
 SIMD命令の利用
21
© 2015 IBM Corporation
IBM Research - Tokyo
命令強度軽減(strength reduction)
 同じ演算を、CPUが持つ速い命令で置き換える。
– 例えば、多くのCPUでは、乗算・除算よりシフト...
© 2015 IBM Corporation
IBM Research - Tokyo
スカラ置き換え(scalar replacement)
 メモリアクセス命令を、単純変数を使った命令に置き換える。
– 後のレジスタ割付で、単純変数がレジ...
© 2015 IBM Corporation
IBM Research - Tokyo
レジスタ割付(register allocation)
 メソッド中の単純変数を、プロセッサが持つレジスタにできるだけ割り付ける。
– レジスタは、1クロ...
© 2015 IBM Corporation
IBM Research - Tokyo
SIMD命令の活用
 SIMD (Single Instruction Multiple Data)命令とは?
– 1つの命令で(Single Instr...
© 2015 IBM Corporation
IBM Research - Tokyo
SIMD命令の活用
 同一処理が、別データに繰り返して適用される部分を、SIMD命令に置き換える
– ループ内での配列アクセス、が代表的
26
for (...
コンパイル時に
実行時情報が利用できたら?
© 2015 IBM Corporation
IBM Research - Tokyo
実行時情報が利用できたら?
 より正確にプログラムの挙動がわかる
– 実行頻度の高いメソッド
– メソッド内の実行頻度の高い実行経路
 より詳しい情報が...
© 2015 IBM Corporation
IBM Research - Tokyo
実行時情報の取得方法
 Sampling – 頻繁に実行されるメソッド、コードの特定
– コンパイルされたコード内の適当な地点(メソッドの入り口、ループの...
© 2015 IBM Corporation
IBM Research - Tokyo
実行情報を利用するための方法
 実行時情報を取得するためのコードを生成してコンパイルしたバイナリを一度
実行して、その情報を元に再コンパイルする
– ユー...
© 2015 IBM Corporation
IBM Research - Tokyo
実行情報を利用した最適化の例
 経路の頻度情報に基づいた最適化
– よく実行されるメソッドに対して、メソッド展開を適用する
– メソッド内のよく実行される...
実行時に
インタプリタがあったら?
© 2015 IBM Corporation
IBM Research - Tokyo
インタプリタとは?
 プログラムを機械語命令にコンパイルすることなく、実行できる
– Ruby、 Python、Perlの代表的な実装で使われている実行方...
© 2015 IBM Corporation
IBM Research - Tokyo
インタプリタとコンパイラが両方あったら
 よく実行されるメソッドだけ、コンパイルすればよいのでは?
– あまり実行されないメソッドは、インタプリタで実行し...
Java言語特有の最適化
© 2015 IBM Corporation
IBM Research - Tokyo
Java言語特有の機能と最適化
 プログラムの安全性を保証するための例外検査
– 冗長な例外検査削除
 プログラムの安全性を保証するための型検査
– 形...
© 2015 IBM Corporation
IBM Research - Tokyo
例外検査とその削除
 プログラムの安全性を保証する手段の1つ
– ポインタの値の検査 – オブジェクトがnullでない
– 配列のインデックスの検査 – ...
© 2015 IBM Corporation
IBM Research - Tokyo
単純に冗長な例外検査削除
 同じ変数に対する、同一の例外検査を削除する。
38
nullcheck a
x = a.f
…      // この間にaの定...
© 2015 IBM Corporation
IBM Research - Tokyo
仮想メソッド呼び出しの高速化と、メソッド展開の適用
 複数のメソッドが呼び出される可能性があり、メソッドの呼び出し先を一意に特
定することが難しい場合
–...
© 2015 IBM Corporation
IBM Research - Tokyo
直接メソッド呼び出し(direct method call)
 直接分岐命令(call address)の使用
– コンパイル時に、プログラム上の情報から...
© 2015 IBM Corporation
IBM Research - Tokyo
仮想メソッド呼び出し(virtual method call)
 実行時に呼び出し先を決定する機構(表引き、など)の使用
– コンパイル時に、呼び出し先が...
© 2015 IBM Corporation
IBM Research - Tokyo
Devirtualizationによって、呼び出し先を特定
 複数の呼び出し先を、特定した1つとその他、に分ける
– 仮想呼び出しではなくなっている(直接...
© 2015 IBM Corporation
IBM Research - Tokyo
Class testによる手法
 元のコード
 直接メソッド呼び出しのコードを呼び出してよいか、
ガードのクラスの一致に関する比較結果に基づいて判断する...
© 2015 IBM Corporation
IBM Research - Tokyo
Guarded devirtualization+メソッド展開による問題点
 メソッド展開した場合と仮想メソッド呼び出しの制御が合流する
– データーフロ...
© 2015 IBM Corporation
IBM Research - Tokyo
末尾複製による合流点の変更
 コードを複製して、制御の合流点の位置を変更して、データフロー解析結果の
精度を下げない(条件分岐におけるrの値は?)
– 場...
© 2015 IBM Corporation
IBM Research - Tokyo
末尾複製に伴うコード削除
 左側のパスは、r = 0が確定しているので、条件分岐を除去可能
– でも、あまり実行されない(=インライン展開
していない)パ...
© 2015 IBM Corporation
IBM Research - Tokyo
(さきほど話した)もしもインタープリタがいたら?
 コンパイルコードとインタープリタ間の実行の遷移について
– インタープリタからコンパイルコードへ
– ...
© 2015 IBM Corporation
IBM Research - Tokyo
Deoptimization
 最適化されたコードから、最適化されていないコード(この場合はインタプリタ)
へ移行する
 コンパイラ内部表現は、条件を満...
© 2015 IBM Corporation
IBM Research - Tokyo
Deoptimizationによるコード最適化
 コンパイルコード内では、rの値が0と決まるので、条件分岐と条件が成立しな
い場合のコードを削除可能
49...
最近のJava処理系とコンパイラの
話題
© 2015 IBM Corporation
IBM Research - Tokyo
最近のJava処理系とコンパイラの話題
 起動時間を短くする
– プログラムの開発時間の短縮
 メモリ消費量を少なくする
– 同じ物理メモリのマシンの元...
© 2015 IBM Corporation
IBM Research - Tokyo
まとめ
 基本的な最適化
 実行時情報を使った最適化
 インタプリタが存在する場合の最適化
 Java言語特有の最適化
 最近のJava処理系とコ...
付録
データフロー解析
© 2015 IBM Corporation
IBM Research - Tokyo
データフロー解析
 プログラム内における変数の定義・参照位置で、取りうる値の集合に関する情報
を特定する。
– 具体的には、CFG上のBBに関するデータフ...
© 2015 IBM Corporation
IBM Research - Tokyo
データフロー解析を使った定数伝搬
 STEP1: プログラムを見て、genとkillを求める
– genは、自分のBBで定義して、自分のBBの出口に定義が...
© 2015 IBM Corporation
IBM Research - Tokyo
データフロー解析を使った定数伝搬
 STEP2: BB1からデータフロー方程式を適用していく
– genとkillが分かっていれば、BB内のことは気にしな...
© 2015 IBM Corporation
IBM Research - Tokyo
データフロー解析を使った定数伝搬
 STEP3: BB2に適用
58
gen[BB1]={s1,s2,s3},
gen[BB2]={s4,s5},
gen...
© 2015 IBM Corporation
IBM Research - Tokyo
データフロー解析を使った定数伝搬
 STEP4: BB3に適用
59
gen[BB1]={s1,s2,s3},
gen[BB2]={s4,s5},
gen...
© 2015 IBM Corporation
IBM Research - Tokyo
データフロー解析を使った定数伝搬
 STEP5: BB4に適用
60
gen[BB1]={s1,s2,s3},
gen[BB2]={s4,s5},
gen...
© 2015 IBM Corporation
IBM Research - Tokyo
データフロー解析を使った定数伝搬
 STEP6: in[BB4]で、xに関する定義はs1とs4が、yに関する定義はs5とs6が、
とそれぞれ複数到達してい...
Nächste SlideShare
Wird geladen in …5
×

20151112 kutech lecture_ishizaki_public

6.824 Aufrufe

Veröffentlicht am

高知工科大学の学部3年生の「コンパイラ」の授業で、2015/11/12に講義させていただいた際の資料です。
コンパイラの基本的な最適化について紹介し、実行時情報を使った最適化、などについても触れています。

Veröffentlicht in: Software
  • Loggen Sie sich ein, um Kommentare anzuzeigen.

20151112 kutech lecture_ishizaki_public

  1. 1. © 2015 IBM Corporation 高知工科大学 情報学群 コンパイラ コンパイラにおける最適化技術 2015年11月12日 石崎 一明 kiszk@acm.org (資料作成協力 井上 拓、緒方 一則、仲池 卓也) 日本アイビーエム(株) 東京基礎研究所 (IBM Research - Tokyo) IBM Research - Tokyo
  2. 2. © 2015 IBM Corporation IBM Research - Tokyo 自己紹介 石崎 一明(いしざき かずあき) http://ibm.co/kiszk 1992年3月 早稲田大学理工学研究科修士課程電気工学専攻を修了。 1992年4月 日本アイ・ビー・エム(株)入社、東京基礎研究所勤務。以来、並列化コンパ イラ、動的コンパイラ、アプリケーション最適化、などの研究に従事。最近は、GPGPUの ためのコンパイル技術の研究に従事。現在、同研究所リサーチ・スタッフ・メンバー。 1995年までは、IBM社の並列化Fortranコンパイラの研究開発をおこなってきました。 1996年以来、IBM社のJava処理系の研究開発にたずさわり、新規に研究開発したコンパ イラ最適化を、IBM社のJava処理系に実装してきました。IBM社のJava処理系は、業界 標準のベンチマークプログラムで、世界最高速を達成してきました。 2002年12月 早稲田大学理工学研究科にて、博士(情報科学)を取得。 2004年情報処理学会業績賞受賞。 2
  3. 3. © 2015 IBM Corporation IBM Research - Tokyo 今日の講義内容  講義内容 – 基本的な最適化 • 実行前コンパイルでも、実行時コンパイルでも使います – 実行時情報を使った最適化 – インタプリタが存在する場合の最適化 – Java言語特有の最適化 – 最近のJava処理系とコンパイラの話題 3
  4. 4. © 2015 IBM Corporation IBM Research - Tokyo コンパイラの構成 中間コード生成 プラットフォーム共通最適化 コード生成 プラットフォーム固有最適化 バイナリ生成 コントロールフローグラフ (CFG) プラットフォーム固有の命令列 レジスタアロケーション、 命令スケジューリング、etc. 字句・構文・意味解析 現在、鵜川先生が授業で 扱われている部分 今日の授業で扱う部分 少しだけここも触れます
  5. 5. 基本的な最適化
  6. 6. © 2015 IBM Corporation IBM Research - Tokyo 最適化(Optimization)とは?  目的 – 実行時間を短縮する • 一台のコンピュータを使って この後はこの最適化について • 複数台のコンピュータを使って(並列化) – メモリ使用量を削減する – 電力消費量を削減する  大前提 – 最適化前のコードと最適化後のコードで、(許されていないかぎり)プログラム の意味を変えないこと • メソッド外部から観測可能な値を変えない –rは観測可能、aとbは観測不可能 • メソッド外部から観測可能なイベントの 発生順序を変えない –Javaプログラムであれば、例外の 発生順序(割算、剰余)を変えないこと 6 int foo(int i, int j) { int a, b, r a = i / j b = j % i r = a + b return r }
  7. 7. © 2015 IBM Corporation IBM Research - Tokyo 最適化の方法  どこを – ソースコードで見える部分 – ソースコードで見えない部分  どの範囲で? – 基本ブロック内 • 基本ブロック(Basic Block, BB) =制御の分岐も合流もない命令列 – 基本ブロック間 • Control Flow Graph(CFG) =プログラムの実行可能な全経路を BBをノードとする有向グラフで示したもの – メソッド間  どうやって? – 命令の実行回数を減らす – 速い命令を使う 7 x = 6 y = x+3 BB4 x = 1 x = 6 BB2 BB3 i > 0 BB1 if (i > 0) { x = 6; } else { x = 1; x = 6; } y = x + 3 CFG
  8. 8. 命令の実行時間を減らす
  9. 9. © 2015 IBM Corporation IBM Research - Tokyo 命令の実行回数を減らす最適化の例  コンパイル時に前もって計算 – 定数の畳込み – 定数伝播  冗長な命令の除去 – 複写伝播 – 不要命令除去  計算結果の再利用 – 部分共通式の削除 – 部分冗長性の削除  コードの移動 – コード上昇 – ループ不変値移動  プログラムの特殊化・専用化 – 末尾複製 – メソッド展開9
  10. 10. © 2015 IBM Corporation IBM Research - Tokyo 定数の畳込み(constant folding)  定数計算をコンパイル時に行う。 – 言語によっては、決められたデータの精度や変数がとることができる値の 範囲に考慮する必要がある • 例:Javaではshortは-32768~32767の値をとるが、-32768/-1の答え は? 10 … x = 2 * 3  … … x = 6 …
  11. 11. © 2015 IBM Corporation IBM Research - Tokyo 定数伝播(constant propagation)  定数値を、後続の文に伝搬させる。 11 x = 6  …      // この間にxの定義がない y = x + 3 x = 6 … y = 6 + 3 x = 6 y = x + 3 BB3 x = 6 BB1 BB2 y = 6 + 3 x = 6 x = 6
  12. 12. © 2015 IBM Corporation IBM Research - Tokyo 複写伝播(copy propagation)  単純な代入命令を、右辺のオペランドに伝搬させる。 12 x = a  // xの定義はここだけ …      // この間にaの定義がない y = x + b x = a … y = a + b y = x + b x = a BB1 z = x + c BB2 BB3 y=a+b z=a+c x = a
  13. 13. © 2015 IBM Corporation IBM Research - Tokyo 不要命令除去(dead code elimination)  メソッド中で使われない値を計算する命令を削除する。  実行されない文を削除する。 13 … x = a + b  // xはこの後使われない … … x = a + b … … if (false) { y = 1 } … if (false) { y = 1 }
  14. 14. © 2015 IBM Corporation IBM Research - Tokyo 共通部分式の削除(common subexpression elimination)  一度計算された式の値を再利用する。 14 x = a*b  … // この間にa,b,xの // 定義がない y = a*b + 3 x = a*b … y = x + 3 x = a*b y = a*b + 3 BB3 x = a*b BB1 BB2 y = x + 3 x = a*b x = a*b
  15. 15. © 2015 IBM Corporation IBM Research - Tokyo 部分冗長性の削除(partial redundancy elimination)  一度計算された式の値を再利用するために、式が存在しなかった実行経路へ、 式を実行順で考えた前方へ移動する。 – 副作用を持つ式の移動に注意 • 例外(0による除算など)を発生する演算 15 x = a*b y = a*b BB3 BB1 BB2 x = a*b t = x y = t BB3 t = a*b BB1 BB2 BB1→BB3、 BB2→BB3の どちらの経路でも、 a*bは1回しか計算されない BB1→BB3の経路を通ると a*bは2回計算される
  16. 16. © 2015 IBM Corporation IBM Research - Tokyo コード上昇(code hoisting)  式を実行順で前方へ移動する。  片方の経路でしか実行されない式でも移動可能 – 副作用を持つ式の移動に注意 16 y = a*s[0] … BB1 z = a*s[0] BB2 BB3 y = t t = a*s[0] BB1 z = t BB2 BB3 a * s[0]の処理に時間がかかる際に、 特に効果が高い
  17. 17. © 2015 IBM Corporation IBM Research - Tokyo ループ内不変値移動(loop invariant code motion)  ループ内で定数の値をループ外へ移動する。 – 副作用を持つ式の移動に注意 17 i = 0 BB3 x += a*b + s[i] i = i + 1   BB1 BB2 i < 88 i = 0 t = a*b x += t + s[i] i = i + 1   i < 88 a*bの計算は1回だけループを回るたびに a*bの計算を行う
  18. 18. © 2015 IBM Corporation IBM Research - Tokyo メソッド展開(method inlining)  メソッド呼び出しの場所に、メソッド本体を展開する。 – 最適化の適用範囲が大きくなる。 – ソースコードには現れない、引数の受け渡しの処理や、メソッド先頭・末尾で のレジスタ待避・回復処理を削除出来る。 18 … x = (a * a) … int foo(int i) { return i * i }  … x = foo(a) … int foo(int i) { return I * I }
  19. 19. © 2015 IBM Corporation IBM Research - Tokyo 末尾複製(tail duplication, splitting)  制御の合流部分以降を複製して、専用コードを作る。 – 変数の性質が一意に決定する、ので他の最適化が使える可能性が増える • BB2→BB4’では、常にa=0、ということが分かる 19 a = 0 BB1 a = foo(a) BB2 BB3 a = 0 BB1 a = foo(a) BB2 BB3 BB4 a < 0 a = ... b < 0 a = ... b < 0 a < 0 a < 0 BB4’ BB4’’
  20. 20. 速い命令を使う
  21. 21. © 2015 IBM Corporation IBM Research - Tokyo 速い命令を使う最適化の例  命令強度軽減  スカラ置き換え  レジスタ割付  SIMD命令の利用 21
  22. 22. © 2015 IBM Corporation IBM Research - Tokyo 命令強度軽減(strength reduction)  同じ演算を、CPUが持つ速い命令で置き換える。 – 例えば、多くのCPUでは、乗算・除算よりシフト・論理積命令の方が高速で ある。 22 … x = a * 8   // 2のべき乗による乗算 y = b % 8   // 2のべき乗による剰余 … … x = a << 3 y = b & 7  …
  23. 23. © 2015 IBM Corporation IBM Research - Tokyo スカラ置き換え(scalar replacement)  メモリアクセス命令を、単純変数を使った命令に置き換える。 – 後のレジスタ割付で、単純変数がレジスタに割り付けられることを期待 23 class A { int f } A a = new A() a.f = 1  // a.fはメモリへのアクセス a.f = a.f * 2 class A { int f } A a = new A() t = 1 t = t * 2 a.f = t
  24. 24. © 2015 IBM Corporation IBM Research - Tokyo レジスタ割付(register allocation)  メソッド中の単純変数を、プロセッサが持つレジスタにできるだけ割り付ける。 – レジスタは、1クロックでデータを読み書きできる高速な(スカラ)データ保持 領域だが、プロセッサによって16または32個と制限がある 24 … a = 1 b = 2 c = a + 3  a = c * 2 … … R1 = 1  // a: R1 [mem_b] = 2  // b: mem R2 = R1 + 3  // c: R2 R1 = R2 * 2 … 変数は3つだが、 レジスタは2個 しかない → aとcに割り当てる
  25. 25. © 2015 IBM Corporation IBM Research - Tokyo SIMD命令の活用  SIMD (Single Instruction Multiple Data)命令とは? – 1つの命令で(Single Instruction)、複数のデータに(Multiple Data)、演算を 行う – 1つのベクタレジスタ内の、複数のデータに、同じ演算を行う、実装が一般 的 25 A0 add input 1 input 2 output add grA,grB,grC vadd vrA,vrB,vrC ベクタレジスタ SIMDの各演算 通常のスカラ命令(1命令1演算) SIMD命令(1命令4演算) B0 C0 A0 add input 1 input 2 output B0 C0 A1 add B1 C1 A2 add B2 C2 A3 add B3 C3 32bit 32bit x 4
  26. 26. © 2015 IBM Corporation IBM Research - Tokyo SIMD命令の活用  同一処理が、別データに繰り返して適用される部分を、SIMD命令に置き換える – ループ内での配列アクセス、が代表的 26 for (i = 0; i < 1024; i++) s[i] = t[i] + u[i] for (i = 0; i < 1024; i += 4) vload  vrB, t[i] // 4データ同時ロード vload  vrC, u[i] // 4データ同時ロード vadd   vrA, vrB, vrC // 4データ同時加算 vstore vrA, s[i]  // 4データ同時ストア 現実は、このような簡単な例ばかりではないので コンパイラによる自動変換は限られた例に限られている
  27. 27. コンパイル時に 実行時情報が利用できたら?
  28. 28. © 2015 IBM Corporation IBM Research - Tokyo 実行時情報が利用できたら?  より正確にプログラムの挙動がわかる – 実行頻度の高いメソッド – メソッド内の実行頻度の高い実行経路  より詳しい情報がわかる – メソッドに渡される引数 – ループの実行回数  より細かいプログラムの情報がわかる – メモリアクセスの際のキャッシュミスは、どこで起きているか 28
  29. 29. © 2015 IBM Corporation IBM Research - Tokyo 実行時情報の取得方法  Sampling – 頻繁に実行されるメソッド、コードの特定 – コンパイルされたコード内の適当な地点(メソッドの入り口、ループの先頭へ のjump等)を通過したときに、ログを取る。 – OSのタイマー割り込みを使って、割り込みハンドラの中で実行されているプ ログラムの命令アドレスを取得する。  Instrumentation – 実行中の値の特定 – コンパイルされたコード内に値を記録するコードを生成する。  Hardware Performance Monitor Counter – キャッシュミス等の特定 – CPUが提供するパフォーマンス・モニタの値(キャッシュミス回数、発生した アドレス)を読み取る。 29
  30. 30. © 2015 IBM Corporation IBM Research - Tokyo 実行情報を利用するための方法  実行時情報を取得するためのコードを生成してコンパイルしたバイナリを一度 実行して、その情報を元に再コンパイルする – ユーザが手動でリコンパイルを行う • Profile Guided Optimization (PGO) in LLVM and GCC – システムが自動的にリコンパイルを行う • 主なJavaの処理系(IBM Java、Open JDK、Oracle JDK、その他) 30 動的コンパイラ メソッドA メソッドB プロセッサ プロファイラ 最初のコンパイル コンパイルリクエスト メソッドB 再々コンパイル 実行時情報 分岐トレースと命令サンプル Call A Call BInst in B 自動的にリコンパイルを行うシステムの例 メソッドB 再コンパイル
  31. 31. © 2015 IBM Corporation IBM Research - Tokyo 実行情報を利用した最適化の例  経路の頻度情報に基づいた最適化 – よく実行されるメソッドに対して、メソッド展開を適用する – メソッド内のよく実行される実行経路の命令数を減らす • あまり実行されない実行経路の命令数は増えるかもしれない  特定した値に基づくコードの特殊化 – 定数伝搬 • 特定しなかった値が来た時のコードも必要  キャッシュミスを減らす – データのプリフェッチ(事前にメモリからキャッシュに読み込んでおく) – データの再配置 • よくアクセスされるデータを近くに配置する 31
  32. 32. 実行時に インタプリタがあったら?
  33. 33. © 2015 IBM Corporation IBM Research - Tokyo インタプリタとは?  プログラムを機械語命令にコンパイルすることなく、実行できる – Ruby、 Python、Perlの代表的な実装で使われている実行方式  実行時コンパイルに比べた利点 – 消費メモリが少ない – プログラム起動の時間が速い  実行時コンパイルに比べた欠点 – プログラムの実行時間が遅い 33
  34. 34. © 2015 IBM Corporation IBM Research - Tokyo インタプリタとコンパイラが両方あったら  よく実行されるメソッドだけ、コンパイルすればよいのでは? – あまり実行されないメソッドは、インタプリタで実行しても、全体の実行時間 に影響を与えない – コンパイルにかかる時間、必要なメモリも減る  さらに、インタプリタに実行時情報を とってもらえばよいのでは? – 実行頻度の高いメソッドなら、 すぐに分かる – 実行頻度の高いメソッド内の経路、 も分かるだろう – 実行中のメソッド内の値、は 記録すると実行時間が遅くなる  ついでに、メソッドをコンパイルする際に、 あまり実行されない部分はインタプリタに 実行をまかせてしまえばよいのでは? – 後ほど、お話します34 クラス管理 オブジェクト 管理 インタープリタ JIT コンパイラ JITコンパイラ 生成コード 実行時 プロファイラ クラスデータ (Java バイト コード) Javaヒープ (Java オブ ジェクト) Javaスタック (ローカル変数、 オペランドスタック) Java仮想マシン(Java VM) Java同期 ランタイム Java例外処理 ランタイム JVM ランタイム
  35. 35. Java言語特有の最適化
  36. 36. © 2015 IBM Corporation IBM Research - Tokyo Java言語特有の機能と最適化  プログラムの安全性を保証するための例外検査 – 冗長な例外検査削除  プログラムの安全性を保証するための型検査 – 形解析による冗長な型検査の削除  多様性(polymorphism)の導入による、仮想メソッド呼び出し – 仮想メソッド呼び出しの高速化と、メソッド展開の適用  カプセル化(encapsulation)による、オブジェクト内のフィールドへの参照 – 脱出解析による、オブジェクトのスタック割付 – スカラ置き換えによる、フィールドアクセスの高速化  言語が提供する同期操作 – 脱出解析による同期操作除去 36
  37. 37. © 2015 IBM Corporation IBM Research - Tokyo 例外検査とその削除  プログラムの安全性を保証する手段の1つ – ポインタの値の検査 – オブジェクトがnullでない – 配列のインデックスの検査 – 配列のインデックスが配列の範囲内である – その他、0での割り算チェックなど  例外検査の削除 – 例外検査のための、比較命令、分岐命令の削除 – 命令の移動機会を増やす • 例外検査に依存する後続の命令は、例外検査より前に移動出来ない 37 void foo(String s) { int h = s.hashCode(); ... int i = s.charAt(88); } Sがnullなら NullPointeException
  38. 38. © 2015 IBM Corporation IBM Research - Tokyo 単純に冗長な例外検査削除  同じ変数に対する、同一の例外検査を削除する。 38 nullcheck a x = a.f …      // この間にaの定義がない nullcheck a y = a.g nullcheck a x = a.f  … nullcheck a y = a.g nullcheck a x = a.f nullcheck a y = a.g BB3 nullcheck a z = a.h BB1 BB2 nullcheck a y = a.g nullcheck a x = a.f nullcheck a z = a.h
  39. 39. © 2015 IBM Corporation IBM Research - Tokyo 仮想メソッド呼び出しの高速化と、メソッド展開の適用  複数のメソッドが呼び出される可能性があり、メソッドの呼び出し先を一意に特 定することが難しい場合 – 仮想メソッド呼び出し – インターフェースメソッド呼び出し 39 call s.add() Sub1.add() Super.add() … ... class Sub1 extends Super { public int add(int i) { ... } public static int calc1(Super s) { ... s.add(2) ... ... } } class Super { public int add(int i) { ... } }
  40. 40. © 2015 IBM Corporation IBM Research - Tokyo 直接メソッド呼び出し(direct method call)  直接分岐命令(call address)の使用 – コンパイル時に、プログラム上の情報から呼び出し先が一意に決定可能 • メソッド展開を適用可能 40 ... call method_div ... method_div: ... ret class Super { public static final int div(int i) { ... } public static int calc() { ... Super.div(2) ... ... } } プログラム 機械語命令
  41. 41. © 2015 IBM Corporation IBM Research - Tokyo 仮想メソッド呼び出し(virtual method call)  実行時に呼び出し先を決定する機構(表引き、など)の使用 – コンパイル時に、呼び出し先が一意に決定できない • メソッド展開ができなくなり、最適化の範囲が狭くなる 41 Super s class Sub1 extends Super { public int add(int i) { ... } public static int calc1(Super s) { ... s.add(2) ... ... } } ... // r3 = object of Super ld r2, (r3+offset_class_in_object) ld r1, (r2+offset_vtableadd_in_class) ld r0, (r1+offset_code_in_method) call r0 ... プログラム 機械語命令 class Sub1 mul foo virtual table add code method Sub1.add r3 r2 r1 binary code r0 sub 同じメソッドはクラス階層内で 同じオフセットを持つ class Super add() class Sub1 add()
  42. 42. © 2015 IBM Corporation IBM Research - Tokyo Devirtualizationによって、呼び出し先を特定  複数の呼び出し先を、特定した1つとその他、に分ける – 仮想呼び出しではなくなっている(直接呼び出し)、のでdevirtualization(脱 仮想化)  分ける方法 – Guarded devirtualization • Class testなど – Direct devirtualization 42 call Sub1.add() s==Sub1 call s.add() call s.add() Sub1.add() Super.add() … ... ガード Y N
  43. 43. © 2015 IBM Corporation IBM Research - Tokyo Class testによる手法  元のコード  直接メソッド呼び出しのコードを呼び出してよいか、 ガードのクラスの一致に関する比較結果に基づいて判断する。 43 Super s ... // r3 = object in s ld r2, offset_class_in_object(r3) ld r1, offset_vtableadd_in_class(r2) ld r0, offset_code_in_method(r3) call r0 ... class Sub1 virtual table add code method Sub1.add // r3 = object in s ld r2, offset_class_in_object(r3) if (r2 == #address_of_classSub1) { call Sub1.add // exec direct call } else { ld r1, offset_vtableadd_in_class(r2) ld r0, offset_code_in_method(r1) call r0 } 機械語命令
  44. 44. © 2015 IBM Corporation IBM Research - Tokyo Guarded devirtualization+メソッド展開による問題点  メソッド展開した場合と仮想メソッド呼び出しの制御が合流する – データーフロー解析結果(rの値は?)の精度が悪くなる →0であるか、値はわからない  解決手段 – 末尾複製 – Deoptimization44 r = Sub1.add(-2) -> -2 + 2 = 0 s==Sub1 r = s.add(-2) class Sub1 extends Super { public int add(int i) { return i + 2; } public static int calc2(Super s) { ... int r = s.add(-2); if (r != 0) { ... // 長いコード } return r; } } r != 0 // 長いコード r = 0 Y
  45. 45. © 2015 IBM Corporation IBM Research - Tokyo 末尾複製による合流点の変更  コードを複製して、制御の合流点の位置を変更して、データフロー解析結果の 精度を下げない(条件分岐におけるrの値は?) – 場合によっては、複製するコード量が 増えることがある 45 r = Sub1.add(-2) -> -2 + 2 = 0 s==Sub1 r = s.add(-2)class Sub1 extends Super { public int add(int i) { return i+2; } public static int calc2(Super s) { ... int r = s.add(-2); if (r != 0) { ... // 長いコード } return r; } } r != 0 // 長いコード r = 0 r != 0 r != 0 Y
  46. 46. © 2015 IBM Corporation IBM Research - Tokyo 末尾複製に伴うコード削除  左側のパスは、r = 0が確定しているので、条件分岐を除去可能 – でも、あまり実行されない(=インライン展開 していない)パスにある、 長いコードをコンパイルしないといけない 46 r = Sub1.add(-2) -> -2 + 2 = 0 s==Sub1 r = s.add(-2)class Sub1 extends Super { public int add(int i) { return i+2; } public static int calc2(Super s) { ... int r = s.add(-2); if (r != 0) { ... // 長いコード } return r; } } r != 0 // 長いコード r = 0 r != 0 Y
  47. 47. © 2015 IBM Corporation IBM Research - Tokyo (さきほど話した)もしもインタープリタがいたら?  コンパイルコードとインタープリタ間の実行の遷移について – インタープリタからコンパイルコードへ – コンパイルコードからインタープリタへ • 通常はメソッドの先頭で遷移する • メソッドの実行途中に遷移できたらうれしいことがある 47 コンパイルされたコード bar() { ... ... } インタープリタのコード foo() { ... bar(); ... } インタープリタのコード foo() { bar() { ... ... bar(); ... ... } } コンパイルされたコード bar() { ... ... }
  48. 48. © 2015 IBM Corporation IBM Research - Tokyo Deoptimization  最適化されたコードから、最適化されていないコード(この場合はインタプリタ) へ移行する  コンパイラ内部表現は、条件を満たさなかったとき、 分岐先がコンパイルコード外となる – コンパイルするコード量が減る – コンパイラコード内の制御合流点が減る  使い方の一例 – Guarded devirtualization+メソッド展開 4848 r = Sub1.add(-2) -> -2 + 2 = 0 s==Sub1 r = s.add(-2) class Sub1 extends Super { public int add(int i) { return i+2; } public static int calc2(Super s) { ... int r = s.add(-2); if (r != 0) { ... // 長いコード } return r; } } r != 0 // 長いコード r = 0 インタプリタで 実行 Y
  49. 49. © 2015 IBM Corporation IBM Research - Tokyo Deoptimizationによるコード最適化  コンパイルコード内では、rの値が0と決まるので、条件分岐と条件が成立しな い場合のコードを削除可能 4949 r = Sub1.add(-2) -> -2 + 2 = 0 s==Sub1 r = s.add(-2) class Sub1 extends Super { public int add(int i) { return i+2; } public static int calc2(Super s) { ... int r = s.add(-2); if (r != 0) { ... // 長いコード } return r; } } インタプリタで 実行 Y
  50. 50. 最近のJava処理系とコンパイラの 話題
  51. 51. © 2015 IBM Corporation IBM Research - Tokyo 最近のJava処理系とコンパイラの話題  起動時間を短くする – プログラムの開発時間の短縮  メモリ消費量を少なくする – 同じ物理メモリのマシンの元で、クラウドやDockerで使えるインスタンス数 が増える  新しいハードウェアを利用して実行時間を短縮する – SIMD – GPGPU  プロセッサをより効率的に使用する – Javaから、Java以外のメモリを操作する • 異なるプロセッサ上で動かしても、同じ方法で操作したい 51
  52. 52. © 2015 IBM Corporation IBM Research - Tokyo まとめ  基本的な最適化  実行時情報を使った最適化  インタプリタが存在する場合の最適化  Java言語特有の最適化  最近のJava処理系とコンパイラの話題 52
  53. 53. 付録
  54. 54. データフロー解析
  55. 55. © 2015 IBM Corporation IBM Research - Tokyo データフロー解析  プログラム内における変数の定義・参照位置で、取りうる値の集合に関する情報 を特定する。 – 具体的には、CFG上のBBに関するデータフロー方程式をたてて解く。 • データフロー方程式の例 –in[b]: BB bの入口に到達する定義の集合 out[b]: BB bの出口にある定義の集合 –解釈: 自分のBB bについて、前に実行されるBBの情報の和を取る 和をとった情報(in[b])を使って、BB bの出口での情報(out[b])を求める  解析結果を用いて、基本ブロック間のある種の最適化を効率良く行うことができる – 定数の畳込み、定数伝播、不要命令除去、複写伝播、など 55
  56. 56. © 2015 IBM Corporation IBM Research - Tokyo データフロー解析を使った定数伝搬  STEP1: プログラムを見て、genとkillを求める – genは、自分のBBで定義して、自分のBBの出口に定義が到達する文 – killは、自分のBBで定義した変数を、BBに関わらず無効にしてしまう文 56 s4: x = 4 s5: y = 5 s7: a = x s8: b = y s9: c = z BB4 s6: y = 6BB2 BB3 BB1 gen[BB1]={s1,s2,s3}, gen[BB2]={s4,s5}, gen[BB3]={s6}, gen[BB4]={s7,s8,s9} kill[BB1]={s4,s5,s6} kill[BB2]={s1,s2,s6} kill[BB3]={s2,s5} kill[BB4]={} s1: x = 1 s2: y = 2 s3: z = 3
  57. 57. © 2015 IBM Corporation IBM Research - Tokyo データフロー解析を使った定数伝搬  STEP2: BB1からデータフロー方程式を適用していく – genとkillが分かっていれば、BB内のことは気にしなくてよい 57 gen[BB1]={s1,s2,s3}, gen[BB2]={s4,s5}, gen[BB3]={s6}, gen[BB4]={s7,s8,s9} kill[BB1]={s4,s5,s6} kill[BB2]={s1,s2,s6} kill[BB3]={s2,s5} kill[BB4]={} in[BB1] = {} s4: x = 4 s5: y = 5 s7: a = x s8: b = y s9: c = z BB4 s6: y = 6BB2 BB3 BB1 s1: x = 1 s2: y = 2 s3: z = 3 out[BB1]= gen[BB1](in[BB1]–kill[BB1]) = {s1,s2,s3}  {} in out BB1  {s1,s2,s3} BB2 BB3 BB4
  58. 58. © 2015 IBM Corporation IBM Research - Tokyo データフロー解析を使った定数伝搬  STEP3: BB2に適用 58 gen[BB1]={s1,s2,s3}, gen[BB2]={s4,s5}, gen[BB3]={s6}, gen[BB4]={s7,s8,s9} kill[BB1]={s4,s5,s6} kill[BB2]={s1,s2,s6} kill[BB3]={s2,s5} kill[BB4]={} s4: x = 4 s5: y = 5 s7: a = x s8: b = y s9: c = z BB4 s6: y = 6BB2 BB3 BB1 s1: x = 1 s2: y = 2 s3: z = 3 in[BB2] = out[BB1] = {s1,s2,s3} out[BB2]= gen[BB2](in[BB2]–kill[BB2]) = {s4,s5}  ({s1,s2,s3}–{s1,s2,s6}) in out BB1  {s1,s2,s3} BB2 {s1,s2,s3} {s3,s4,s5} BB3 BB4
  59. 59. © 2015 IBM Corporation IBM Research - Tokyo データフロー解析を使った定数伝搬  STEP4: BB3に適用 59 gen[BB1]={s1,s2,s3}, gen[BB2]={s4,s5}, gen[BB3]={s6}, gen[BB4]={s7,s8,s9} kill[BB1]={s4,s5,s6} kill[BB2]={s1,s2,s6} kill[BB3]={s2,s5} kill[BB4]={} s4: x = 4 s5: y = 5 s7: a = x s8: b = y s9: c = z BB4 s6: y = 6BB2 BB3 BB1 s1: x = 1 s2: y = 2 s3: z = 3 in[BB3] = out[BB1] = {s1,s2,s3} out[BB3]= gen[BB3](in[BB3]–kill[BB3]) = {s6}  ({s1,s2,s3}–{s2,s5}) in out BB1  {s1,s2,s3} BB2 {s1,s2,s3} {s3,s4,s5} BB3 {s1,s2,s3} {s1,s3,s6} BB4
  60. 60. © 2015 IBM Corporation IBM Research - Tokyo データフロー解析を使った定数伝搬  STEP5: BB4に適用 60 gen[BB1]={s1,s2,s3}, gen[BB2]={s4,s5}, gen[BB3]={s6}, gen[BB4]={s7,s8,s9} kill[BB1]={s4,s5,s6} kill[BB2]={s1,s2,s6} kill[BB3]={s2,s5} kill[BB4]={} s4: x = 4 s5: y = 5 s7: a = x s8: b = y s9: c = z BB4 s6: y = 6BB2 BB3 BB1 s1: x = 1 s2: y = 2 s3: z = 3 in[BB4] = out[BB2]out[BB3]= {s3,s4,s5}{s1,s3,s6} in out BB1  {s1,s2,s3} BB2 {s1,s2,s3} {s3,s4,s5} BB3 {s1,s2,s3} {s1,s3,s6} BB4 {s1,s3,s4,s5,s6} {...} out[BB4]= gen[BB4](in[BB4]–kill[BB4]) = ...
  61. 61. © 2015 IBM Corporation IBM Research - Tokyo データフロー解析を使った定数伝搬  STEP6: in[BB4]で、xに関する定義はs1とs4が、yに関する定義はs5とs6が、 とそれぞれ複数到達している。 zに関する定義は、唯一s3だけが到達しているので、 BB4におけるzの使用をs3の定義で置き換える。 61 s4: x = 4 s5: y = 5 s7: a = x s8: b = y s9: c = z3 BB4 s6: y = 6BB2 BB3 BB1 s1: x = 1 s2: y = 2 s3: z = 3 in out BB1  {s1,s2,s3} BB2 {s1,s2,s3} {s3,s4,s5} BB3 {s1,s2,s3} {s1,s3,s6} BB4 {s1,s3,s4,s5,s6} {...}

×