20151112 kutech lecture_ishizaki_public

61
© 2015 IBM Corporation 高知工科大学 情報学群 コンパイラ コンパイラにおける最適化技術 20151112石崎 一明 [email protected] (資料作成協力 井上 拓、緒方 一則、仲池 卓也) 日本アイビーエム(株) 東京基礎研究所 (IBM Research - Tokyo) IBM Research - Tokyo

Upload: kazuaki-ishizaki

Post on 14-Apr-2017

4.408 views

Category:

Software


0 download

TRANSCRIPT

Page 1: 20151112 kutech lecture_ishizaki_public

© 2015 IBM Corporation

高知工科大学 情報学群 コンパイラ

コンパイラにおける最適化技術

2015年11月12日石崎 一明 [email protected](資料作成協力 井上 拓、緒方 一則、仲池 卓也)日本アイビーエム(株) 東京基礎研究所 (IBM Research - Tokyo)

IBM Research - Tokyo

Page 2: 20151112 kutech lecture_ishizaki_public

© 2015 IBM Corporation

IBM Research - Tokyo

自己紹介

石崎 一明(いしざき かずあき) http://ibm.co/kiszk1992年3月 早稲田大学理工学研究科修士課程電気工学専攻を修了。

1992年4月 日本アイ・ビー・エム(株)入社、東京基礎研究所勤務。以来、並列化コンパイラ、動的コンパイラ、アプリケーション最適化、などの研究に従事。最近は、GPGPUのためのコンパイル技術の研究に従事。現在、同研究所リサーチ・スタッフ・メンバー。

1995年までは、IBM社の並列化Fortranコンパイラの研究開発をおこなってきました。

1996年以来、IBM社のJava処理系の研究開発にたずさわり、新規に研究開発したコンパイラ最適化を、IBM社のJava処理系に実装してきました。IBM社のJava処理系は、業界標準のベンチマークプログラムで、世界最高速を達成してきました。

2002年12月 早稲田大学理工学研究科にて、博士(情報科学)を取得。

2004年情報処理学会業績賞受賞。

2

Page 3: 20151112 kutech lecture_ishizaki_public

© 2015 IBM Corporation

IBM Research - Tokyo

今日の講義内容

講義内容

– 基本的な最適化

• 実行前コンパイルでも、実行時コンパイルでも使います

– 実行時情報を使った最適化

– インタプリタが存在する場合の最適化

– Java言語特有の最適化

– 最近のJava処理系とコンパイラの話題

3

Page 4: 20151112 kutech lecture_ishizaki_public

© 2015 IBM Corporation

IBM Research - Tokyo

コンパイラの構成

中間コード生成

プラットフォーム共通最適化

コード生成

プラットフォーム固有最適化

バイナリ生成

コントロールフローグラフ(CFG)

プラットフォーム固有の命令列

レジスタアロケーション、命令スケジューリング、etc.

字句・構文・意味解析現在、鵜川先生が授業で扱われている部分

今日の授業で扱う部分

少しだけここも触れます

Page 5: 20151112 kutech lecture_ishizaki_public

基本的な最適化

Page 6: 20151112 kutech lecture_ishizaki_public

© 2015 IBM Corporation

IBM Research - Tokyo

最適化(Optimization)とは?

目的

– 実行時間を短縮する

• 一台のコンピュータを使って この後はこの最適化について

• 複数台のコンピュータを使って(並列化)

– メモリ使用量を削減する

– 電力消費量を削減する

大前提

– 最適化前のコードと最適化後のコードで、(許されていないかぎり)プログラムの意味を変えないこと

• メソッド外部から観測可能な値を変えない

–rは観測可能、aとbは観測不可能

• メソッド外部から観測可能なイベントの発生順序を変えない

–Javaプログラムであれば、例外の発生順序(割算、剰余)を変えないこと

6

int foo(int i, int j) {int a, b, ra = i / jb = j % ir = a + breturn r

}

Page 7: 20151112 kutech lecture_ishizaki_public

© 2015 IBM Corporation

IBM Research - Tokyo

最適化の方法

どこを

– ソースコードで見える部分

– ソースコードで見えない部分

どの範囲で?

– 基本ブロック内

•基本ブロック(Basic Block, BB)=制御の分岐も合流もない命令列

– 基本ブロック間

•Control Flow Graph(CFG)=プログラムの実行可能な全経路をBBをノードとする有向グラフで示したもの

– メソッド間

どうやって?

– 命令の実行回数を減らす

– 速い命令を使う7

x = 6

y = x+3BB4

x = 1x = 6

BB2 BB3

i > 0BB1

if (i > 0) {x = 6;

} else {x = 1;x = 6;

}y = x + 3

CFG

Page 8: 20151112 kutech lecture_ishizaki_public

命令の実行時間を減らす

Page 9: 20151112 kutech lecture_ishizaki_public

© 2015 IBM Corporation

IBM Research - Tokyo

命令の実行回数を減らす最適化の例

コンパイル時に前もって計算

– 定数の畳込み

– 定数伝播

冗長な命令の除去

– 複写伝播

– 不要命令除去

計算結果の再利用

– 部分共通式の削除

– 部分冗長性の削除

コードの移動

– コード上昇

– ループ不変値移動

プログラムの特殊化・専用化

– 末尾複製

– メソッド展開9

Page 10: 20151112 kutech lecture_ishizaki_public

© 2015 IBM Corporation

IBM Research - Tokyo

定数の畳込み(constant folding)

定数計算をコンパイル時に行う。

– 言語によっては、決められたデータの精度や変数がとることができる値の範囲に考慮する必要がある

• 例:Javaではshortは-32768~32767の値をとるが、-32768/-1の答えは?

10

x = 2 * 3 

x = 6

Page 11: 20151112 kutech lecture_ishizaki_public

© 2015 IBM Corporation

IBM Research - Tokyo

定数伝播(constant propagation)

定数値を、後続の文に伝搬させる。

11

x = 6 

…      // この間にxの定義がない

y = x + 3

x = 6

y = 6 + 3

x = 6

y = x + 3BB3

x = 6BB1 BB2

y = 6 + 3

x = 6 x = 6

Page 12: 20151112 kutech lecture_ishizaki_public

© 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 = aBB1

z = x + cBB2 BB3

y=a+b z=a+c

x = a

Page 13: 20151112 kutech lecture_ishizaki_public

© 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

}

Page 14: 20151112 kutech lecture_ishizaki_public

© 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 + 3BB3

x = a*bBB1 BB2

y = x + 3

x = a*b x = a*b

Page 15: 20151112 kutech lecture_ishizaki_public

© 2015 IBM Corporation

IBM Research - Tokyo

部分冗長性の削除(partial redundancy elimination)

一度計算された式の値を再利用するために、式が存在しなかった実行経路へ、式を実行順で考えた前方へ移動する。

– 副作用を持つ式の移動に注意

• 例外(0による除算など)を発生する演算

15

x = a*b

y = a*bBB3

BB1 BB2x = a*bt = x

y = tBB3

t = a*b

BB1 BB2

BB1→BB3、 BB2→BB3のどちらの経路でも、a*bは1回しか計算されない

BB1→BB3の経路を通るとa*bは2回計算される

Page 16: 20151112 kutech lecture_ishizaki_public

© 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]の処理に時間がかかる際に、特に効果が高い

Page 17: 20151112 kutech lecture_ishizaki_public

© 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 = 0t = a*b

x += t + s[i]i = i + 1  

i < 88

a*bの計算は1回だけループを回るたびにa*bの計算を行う

Page 18: 20151112 kutech lecture_ishizaki_public

© 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

}

Page 19: 20151112 kutech lecture_ishizaki_public

© 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

BB4a < 0

a = ...b < 0

a = ...b < 0

a < 0 a < 0BB4’ BB4’’

Page 20: 20151112 kutech lecture_ishizaki_public

速い命令を使う

Page 21: 20151112 kutech lecture_ishizaki_public

© 2015 IBM Corporation

IBM Research - Tokyo

速い命令を使う最適化の例

命令強度軽減

スカラ置き換え

レジスタ割付

SIMD命令の利用

21

Page 22: 20151112 kutech lecture_ishizaki_public

© 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 

Page 23: 20151112 kutech lecture_ishizaki_public

© 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

Page 24: 20151112 kutech lecture_ishizaki_public

© 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に割り当てる

Page 25: 20151112 kutech lecture_ishizaki_public

© 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

Page 26: 20151112 kutech lecture_ishizaki_public

© 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データ同時ストア

現実は、このような簡単な例ばかりではないのでコンパイラによる自動変換は限られた例に限られている

Page 27: 20151112 kutech lecture_ishizaki_public

コンパイル時に実行時情報が利用できたら?

Page 28: 20151112 kutech lecture_ishizaki_public

© 2015 IBM Corporation

IBM Research - Tokyo

実行時情報が利用できたら?

より正確にプログラムの挙動がわかる

– 実行頻度の高いメソッド

– メソッド内の実行頻度の高い実行経路

より詳しい情報がわかる

– メソッドに渡される引数

– ループの実行回数

より細かいプログラムの情報がわかる

– メモリアクセスの際のキャッシュミスは、どこで起きているか

28

Page 29: 20151112 kutech lecture_ishizaki_public

© 2015 IBM Corporation

IBM Research - Tokyo

実行時情報の取得方法

Sampling – 頻繁に実行されるメソッド、コードの特定

– コンパイルされたコード内の適当な地点(メソッドの入り口、ループの先頭へのjump等)を通過したときに、ログを取る。

– OSのタイマー割り込みを使って、割り込みハンドラの中で実行されているプログラムの命令アドレスを取得する。

Instrumentation – 実行中の値の特定

– コンパイルされたコード内に値を記録するコードを生成する。

Hardware Performance Monitor Counter – キャッシュミス等の特定

– CPUが提供するパフォーマンス・モニタの値(キャッシュミス回数、発生したアドレス)を読み取る。

29

Page 30: 20151112 kutech lecture_ishizaki_public

© 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

再コンパイル

Page 31: 20151112 kutech lecture_ishizaki_public

© 2015 IBM Corporation

IBM Research - Tokyo

実行情報を利用した最適化の例

経路の頻度情報に基づいた最適化

– よく実行されるメソッドに対して、メソッド展開を適用する

– メソッド内のよく実行される実行経路の命令数を減らす

• あまり実行されない実行経路の命令数は増えるかもしれない

特定した値に基づくコードの特殊化

– 定数伝搬

• 特定しなかった値が来た時のコードも必要

キャッシュミスを減らす

– データのプリフェッチ(事前にメモリからキャッシュに読み込んでおく)

– データの再配置

• よくアクセスされるデータを近くに配置する

31

Page 32: 20151112 kutech lecture_ishizaki_public

実行時にインタプリタがあったら?

Page 33: 20151112 kutech lecture_ishizaki_public

© 2015 IBM Corporation

IBM Research - Tokyo

インタプリタとは?

プログラムを機械語命令にコンパイルすることなく、実行できる

– Ruby、 Python、Perlの代表的な実装で使われている実行方式

実行時コンパイルに比べた利点

– 消費メモリが少ない

– プログラム起動の時間が速い

実行時コンパイルに比べた欠点

– プログラムの実行時間が遅い

33

Page 34: 20151112 kutech lecture_ishizaki_public

© 2015 IBM Corporation

IBM Research - Tokyo

インタプリタとコンパイラが両方あったら

よく実行されるメソッドだけ、コンパイルすればよいのでは?

– あまり実行されないメソッドは、インタプリタで実行しても、全体の実行時間に影響を与えない

– コンパイルにかかる時間、必要なメモリも減る

さらに、インタプリタに実行時情報をとってもらえばよいのでは?

– 実行頻度の高いメソッドなら、すぐに分かる

– 実行頻度の高いメソッド内の経路、も分かるだろう

– 実行中のメソッド内の値、は記録すると実行時間が遅くなる

ついでに、メソッドをコンパイルする際に、あまり実行されない部分はインタプリタに実行をまかせてしまえばよいのでは?

– 後ほど、お話します34

クラス管理オブジェクト

管理

インタープリタJIT

コンパイラJITコンパイラ

生成コード

実行時プロファイラ

クラスデータ(Java バイト

コード)

Javaヒープ(Java オブジェクト)

Javaスタック(ローカル変数、

オペランドスタック)

Java仮想マシン(Java VM)

Java同期ランタイム

Java例外処理ランタイム

JVMランタイム

Page 35: 20151112 kutech lecture_ishizaki_public

Java言語特有の最適化

Page 36: 20151112 kutech lecture_ishizaki_public

© 2015 IBM Corporation

IBM Research - Tokyo

Java言語特有の機能と最適化

プログラムの安全性を保証するための例外検査

– 冗長な例外検査削除

プログラムの安全性を保証するための型検査

– 形解析による冗長な型検査の削除

多様性(polymorphism)の導入による、仮想メソッド呼び出し

– 仮想メソッド呼び出しの高速化と、メソッド展開の適用

カプセル化(encapsulation)による、オブジェクト内のフィールドへの参照

– 脱出解析による、オブジェクトのスタック割付

– スカラ置き換えによる、フィールドアクセスの高速化

言語が提供する同期操作

– 脱出解析による同期操作除去

36

Page 37: 20151112 kutech lecture_ishizaki_public

© 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

Page 38: 20151112 kutech lecture_ishizaki_public

© 2015 IBM Corporation

IBM Research - Tokyo

単純に冗長な例外検査削除

同じ変数に対する、同一の例外検査を削除する。

38

nullcheck ax = a.f…      // この間にaの定義がない

nullcheck ay = a.g

nullcheck ax = a.f …nullcheck ay = a.g

nullcheck ax = a.f

nullcheck ay = a.g

BB3

nullcheck az = a.h

BB1 BB2

nullcheck ay = a.g

nullcheck ax = a.f

nullcheck az = a.h

Page 39: 20151112 kutech lecture_ishizaki_public

© 2015 IBM Corporation

IBM Research - Tokyo

仮想メソッド呼び出しの高速化と、メソッド展開の適用

複数のメソッドが呼び出される可能性があり、メソッドの呼び出し先を一意に特定することが難しい場合

– 仮想メソッド呼び出し

– インターフェースメソッド呼び出し

39

call s.add() Sub1.add()Super.add()

…... class Sub1 extends Super {

publicint add(int i) { ... }

public staticint calc1(Super s) {... s.add(2) ......

}}

class Super {publicint add(int i) { ... }

}

Page 40: 20151112 kutech lecture_ishizaki_public

© 2015 IBM Corporation

IBM Research - Tokyo

直接メソッド呼び出し(direct method call)直接分岐命令(call address)の使用

– コンパイル時に、プログラム上の情報から呼び出し先が一意に決定可能

• メソッド展開を適用可能

40

...call method_div...

method_div:...ret

class Super {public static finalint div(int i) { ... }

public static int calc() {... Super.div(2) ......

}}

プログラム 機械語命令

Page 41: 20151112 kutech lecture_ishizaki_public

© 2015 IBM Corporation

IBM Research - Tokyo

仮想メソッド呼び出し(virtual method call)実行時に呼び出し先を決定する機構(表引き、など)の使用

– コンパイル時に、呼び出し先が一意に決定できない

• メソッド展開ができなくなり、最適化の範囲が狭くなる

41

Super s

class Sub1 extends Super {publicint add(int i) { ... }

public staticint calc1(Super s) {... s.add(2) ......

}}

...// r3 = object of Superld 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.addr3 r2 r1

binary code

r0sub同じメソッドはクラス階層内で同じオフセットを持つ

class Superadd()

class Sub1add()

Page 42: 20151112 kutech lecture_ishizaki_public

© 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

Page 43: 20151112 kutech lecture_ishizaki_public

© 2015 IBM Corporation

IBM Research - Tokyo

Class testによる手法

元のコード

直接メソッド呼び出しのコードを呼び出してよいか、ガードのクラスの一致に関する比較結果に基づいて判断する。

43

Super s

...// r3 = object in sld 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 sld 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

}

機械語命令

Page 44: 20151112 kutech lecture_ishizaki_public

© 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

Page 45: 20151112 kutech lecture_ishizaki_public

© 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

Page 46: 20151112 kutech lecture_ishizaki_public

© 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

Page 47: 20151112 kutech lecture_ishizaki_public

© 2015 IBM Corporation

IBM Research - Tokyo

(さきほど話した)もしもインタープリタがいたら?

コンパイルコードとインタープリタ間の実行の遷移について

– インタープリタからコンパイルコードへ

– コンパイルコードからインタープリタへ

• 通常はメソッドの先頭で遷移する

• メソッドの実行途中に遷移できたらうれしいことがある

47

コンパイルされたコードbar() {......

}

インタープリタのコードfoo() {...bar();...

}

インタープリタのコードfoo() { bar() {... ...bar(); ...... }

}

コンパイルされたコードbar() {... ...

}

Page 48: 20151112 kutech lecture_ishizaki_public

© 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

Page 49: 20151112 kutech lecture_ishizaki_public

© 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

Page 50: 20151112 kutech lecture_ishizaki_public

最近のJava処理系とコンパイラの話題

Page 51: 20151112 kutech lecture_ishizaki_public

© 2015 IBM Corporation

IBM Research - Tokyo

最近のJava処理系とコンパイラの話題

起動時間を短くする

– プログラムの開発時間の短縮

メモリ消費量を少なくする

– 同じ物理メモリのマシンの元で、クラウドやDockerで使えるインスタンス数が増える

新しいハードウェアを利用して実行時間を短縮する

– SIMD

– GPGPU

プロセッサをより効率的に使用する

– Javaから、Java以外のメモリを操作する

• 異なるプロセッサ上で動かしても、同じ方法で操作したい

51

Page 52: 20151112 kutech lecture_ishizaki_public

© 2015 IBM Corporation

IBM Research - Tokyo

まとめ

基本的な最適化

実行時情報を使った最適化

インタプリタが存在する場合の最適化

Java言語特有の最適化

最近のJava処理系とコンパイラの話題

52

Page 53: 20151112 kutech lecture_ishizaki_public

付録

Page 54: 20151112 kutech lecture_ishizaki_public

データフロー解析

Page 55: 20151112 kutech lecture_ishizaki_public

© 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

Page 56: 20151112 kutech lecture_ishizaki_public

© 2015 IBM Corporation

IBM Research - Tokyo

データフロー解析を使った定数伝搬

STEP1: プログラムを見て、genとkillを求める

– genは、自分のBBで定義して、自分のBBの出口に定義が到達する文

– killは、自分のBBで定義した変数を、BBに関わらず無効にしてしまう文

56

s4: x = 4s5: y = 5

s7: a = xs8: b = ys9: c = z

BB4

s6: y = 6BB2 BB3

BB1gen[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 = 1s2: y = 2s3: z = 3

Page 57: 20151112 kutech lecture_ishizaki_public

© 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 = 4s5: y = 5

s7: a = xs8: b = ys9: c = z

BB4

s6: y = 6BB2 BB3

BB1 s1: x = 1s2: y = 2s3: z = 3 out[BB1]= gen[BB1](in[BB1]–kill[BB1])

= {s1,s2,s3} {}

in out

BB1 {s1,s2,s3}

BB2

BB3

BB4

Page 58: 20151112 kutech lecture_ishizaki_public

© 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 = 4s5: y = 5

s7: a = xs8: b = ys9: c = z

BB4

s6: y = 6BB2 BB3

BB1 s1: x = 1s2: y = 2s3: 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

Page 59: 20151112 kutech lecture_ishizaki_public

© 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 = 4s5: y = 5

s7: a = xs8: b = ys9: c = z

BB4

s6: y = 6BB2 BB3

BB1 s1: x = 1s2: y = 2s3: 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

Page 60: 20151112 kutech lecture_ishizaki_public

© 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 = 4s5: y = 5

s7: a = xs8: b = ys9: c = z

BB4

s6: y = 6BB2 BB3

BB1 s1: x = 1s2: y = 2s3: 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])= ...

Page 61: 20151112 kutech lecture_ishizaki_public

© 2015 IBM Corporation

IBM Research - Tokyo

データフロー解析を使った定数伝搬

STEP6: in[BB4]で、xに関する定義はs1とs4が、yに関する定義はs5とs6が、とそれぞれ複数到達している。zに関する定義は、唯一s3だけが到達しているので、BB4におけるzの使用をs3の定義で置き換える。

61

s4: x = 4s5: y = 5

s7: a = xs8: b = ys9: c = z3

BB4

s6: y = 6BB2 BB3

BB1 s1: x = 1s2: y = 2s3: 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} {...}