Lazy Compilation

first-published: Apr 30 14:15, 2000

last-modified: Aug 29 17:00, 2000


Java Just-In-Time (JIT)コンパイラでの Lazy Compilation の効果を示します。

Lazy Compilation

JITコンパイラは基本的に、あるメソッドが呼び出された時点 でそのメソッドを JITコンパイルします。別にクラスがロード された時点でそのクラスの全メソッドをコンパイルしてもいい のですが、今後実行されるかどうかわからないメソッドをコン パイルしてしまうとたいてい損をします。JITコンパイルはプ ログラムの実行時に行われるため、コンパイルにかかる時間が そのまま実行時間に加わりますし、生成されたネイティブコー ドが消費するメモリもばかになりません。

初めて呼び出された瞬間にコンパイルしてもいいのですが、コ ンパイルしたメソッドがその一回限りしか呼び出されずに結局 損をしてしまうことがままあります。そこで、何回か呼び出さ れた時点ではじめてそのメソッドをコンパイルする、というこ とがよく行われます。このようにコンパイルのタイミングを遅 らせることを Lazy Compilation と呼びます。これは、実行時情報を活用 したコンパイル一般を指す Adaptive Compilation (適応的コンパイル) の一種です。Lazy Compilation は、これまで何回も呼ばれてきたメソッドは今後 も何回も呼ばれるだろうから、コンパイルしておけば得をする に違いない、という経験則に基づいています。

実験

shuJIT 0.6.3 と Blackdown JDK 1.2.2 FCS、Linux を用いて、Lazy Compilation によって どのくらいのコンパイルを節約できるのかを示します。shuJIT は何種類かのオプショ ン指定を受け付けます。cmplthreshold オプショ ンが Lazy Compilation のためのオプションです。このオプショ ンが指定されると、メソッドは指定された回数(しきい値)だけ 呼び出された時点で初めてコンパイルされるようになります。 また、codesize オプションを用いることで、JITコ ンパイルされたメソッドの数や、生成されたネイティブコード の量を知ることができます。

しきい値の設定をいろいろと変えてみて、下記のごく簡単なアプレットを実行した際に

をグラフで示します。
public class HelloWorld extends Applet {
  public void init() {
    resize(150, 30);
  }

  public void paint(Graphics g) {
    g.drawString("Hello world.", 50, 25);
    System.exit(0);
  }
}

図1: コンパイルされたメソッド数
コンパイルされたメソッド数
図2: 生成されたネイティブコードのバイト数 (キロバイト)
生成されたコード量

図2 を見ると、Lazy Compilation を行わない場合 (しきい値 が 1) には 1327 KB のネイティブコードが生成されていると ころ、しきい値を 2 にすると 726 KB と大きく減っているこ とが判ります。しきい値がある程度大きくなると、1, 2 増減 させたところで効果はほとんど変わらないことも判ります。

参考

Borland 社の JIT コンパイラ (javacomp-1.2.15) は、このし きい値を 2に設定しています。

Sun の HotSpot VM では、メソッド呼び出し時だけでなく後方 ジャンプ時にもメソッド呼び出しカウンタを増やします。しき い値を越えた場合はメソッド実行中であっても JITコンパイル を行い、メソッドの途中からネイティブコードの実行に切替え ることも可能となっています。これにより、インタプリタで実 行を始めたのだけれど実は繰り返し回数の多いループがあって… コンパイルしておけばよかった!という場合にも対応できるよ うになっています。逆に、JITコンパイラが生成したネイティ ブコードの実行中であってもインタプリタによる実行に切替え ることもできます (on stack replacement)。これにより、ま さにネイティブコードが実行されている最中のメソッドを再コ ンパイルすることも可能となっています。最適化のし直しに有 効です。


Back to