インスタンス変数へのアクセスより、ローカル変数へアクセス

private Model model;
public void updateModel() {
    Model m = this.model;
    // 更新処理
}

今日、上のようなコードを書いてたら、同僚から「なんでこんなことしてるんですか?」と聞かれたので。
インスタンス変数にアクセスするより、ローカル変数に代入してアクセスした方がパフォーマンス上がるらしいですよ。確か、ローカル変数はスタックに積まれて、インスタンス変数はヒープにありそうで、その辺の違いっすかねーと、濁してきた。

どっかの本で見た気がするんだけど思い出せない。ぐぐってみるとIBMのサイトにそれっぽいのがあった。
http://www.ibm.com/developerworks/jp/java/library/j-praxis/pr35.html

てか、上のコードでもパフォーマンス違うのかなーと思い、テスト。

class Test {
  private Model model = new Model();
  public static void main(String args[]) {
    Test t = new Test();
    t.updateInstanceModel();
    t.updateLocalModel();
  }

  // インスタンス変数に直接アクセス
  public void updateInstanceModel() {
    long start = System.currentTimeMillis();
    for (int i = 0; i < 1000000000; i++) {
      model.id = 10;
      model.name = "jhon";
    }
    long end = System.currentTimeMillis();
    System.out.println("updateInstanceModel: " + (end - start));
  }
  
  // ローカル変数へからのアクセス
  public void updateLocalModel() {
    Model m = this.model;
    long start = System.currentTimeMillis();
    for (int i = 0; i < 1000000000; i++) {
      m.id = 10;
      m.name = "jhon";
    }
    long end = System.currentTimeMillis();
    System.out.println("updateLocalModel: " + (end - start));
  }
}

TestクラスからアクセスされるModelクラス

class Model {
  public int id;
  public String name;
}

javapコマンドで逆アセンブル

a$ javap -c Test
Compiled from "Test.java"
class Test extends java.lang.Object{
Test();
  Code:
   0:	aload_0
   1:	invokespecial	#1; //Method java/lang/Object."<init>":()V
   4:	aload_0
   5:	new	#2; //class Model
   8:	dup
   9:	invokespecial	#3; //Method Model."<init>":()V
   12:	putfield	#4; //Field model:LModel;
   15:	return

public static void main(java.lang.String[]);
  Code:
   0:	new	#5; //class Test
   3:	dup
   4:	invokespecial	#6; //Method "<init>":()V
   7:	astore_1
   8:	aload_1
   9:	invokevirtual	#7; //Method updateInstanceModel:()V
   12:	aload_1
   13:	invokevirtual	#8; //Method updateLocalModel:()V
   16:	return

public void updateInstanceModel();
  Code:
   0:	invokestatic	#9; //Method java/lang/System.currentTimeMillis:()J
   3:	lstore_1
   4:	iconst_0
   5:	istore_3
   6:	iload_3
   7:	ldc	#10; //int 1000000000
   9:	if_icmpge	36
   12:	aload_0
   13:	getfield	#4; //Field model:LModel;
   16:	bipush	10
   18:	putfield	#11; //Field Model.id:I
   21:	aload_0
   22:	getfield	#4; //Field model:LModel;
   25:	ldc	#12; //String jhon
   27:	putfield	#13; //Field Model.name:Ljava/lang/String;
   30:	iinc	3, 1
   33:	goto	6
   36:	invokestatic	#9; //Method java/lang/System.currentTimeMillis:()J
   39:	lstore_3
   40:	getstatic	#14; //Field java/lang/System.out:Ljava/io/PrintStream;
   43:	new	#15; //class java/lang/StringBuilder
   46:	dup
   47:	invokespecial	#16; //Method java/lang/StringBuilder."<init>":()V
   50:	ldc	#17; //String updateInstanceModel: 
   52:	invokevirtual	#18; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   55:	lload_3
   56:	lload_1
   57:	lsub
   58:	invokevirtual	#19; //Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
   61:	invokevirtual	#20; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   64:	invokevirtual	#21; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   67:	return

public void updateLocalModel();
  Code:
   0:	aload_0
   1:	getfield	#4; //Field model:LModel;
   4:	astore_1
   5:	invokestatic	#9; //Method java/lang/System.currentTimeMillis:()J
   8:	lstore_2
   9:	iconst_0
   10:	istore	4
   12:	iload	4
   14:	ldc	#10; //int 1000000000
   16:	if_icmpge	37
   19:	aload_1
   20:	bipush	10
   22:	putfield	#11; //Field Model.id:I
   25:	aload_1
   26:	ldc	#12; //String jhon
   28:	putfield	#13; //Field Model.name:Ljava/lang/String;
   31:	iinc	4, 1
   34:	goto	12
   37:	invokestatic	#9; //Method java/lang/System.currentTimeMillis:()J
   40:	lstore	4
   42:	getstatic	#14; //Field java/lang/System.out:Ljava/io/PrintStream;
   45:	new	#15; //class java/lang/StringBuilder
   48:	dup
   49:	invokespecial	#16; //Method java/lang/StringBuilder."<init>":()V
   52:	ldc	#22; //String updateLocalModel: 
   54:	invokevirtual	#18; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   57:	lload	4
   59:	lload_2
   60:	lsub
   61:	invokevirtual	#19; //Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
   64:	invokevirtual	#20; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   67:	invokevirtual	#21; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   70:	return

}

うーん。何か違うが、実際はどうなのかというと。何回か実行して処理時間の差をとってみた。こんな計測じゃ微妙かもしれんが。。

1回目
updateInstanceModel: 4856
updateLocalModel: 3640
2回目
updateInstanceModel: 4564
updateLocalModel: 3922
3回目
updateInstanceModel: 4747
updateLocalModel: 3541
4回目 
updateInstanceModel: 5068
updateLocalModel: 3621
5回目
updateInstanceModel: 4704
updateLocalModel: 3615

お、ローカル変数に入れたら早いっぽい。(てか、そこまで気をつかう程の差は出てない気がするけどそれは言わない約束)
とここまでやったのはいいけど、逆アセンブルしたコードが読めない;;ま、とりあえず今日はこの辺で。(結局納得してもらえる説明はできそうにない。。)