継承によるパフォーマンス劣化の落とし穴

あるインターフェースを実装している異なる2つのクラスを生成し、
メソッドを実行する場合にメソッド呼び出しコストが増大する。


2つのクラスインスタンス生成でのバイトコードでの影響はほとんどない。
スタック位置が異なる。
※javapで確認


推測になってしまうが invokevirtual でのメソッド検索処理時間コストが高くなっている。


事象:同じインターフェースを実装した2つのクラスを生成し、メソッド呼び出しを行う場合はメソッド検索コストがかかる。
※同じインターフェースを実装した1つのクラスを2つ生成してもパフォーマンスは劣化しなかった。

教訓:不要なインスタンスの生成は行わない。


以下 実証コード

public class A {
	private final A1 a;

	public A(A1 a) {
		this.a = a;
	}

	public void execute() {
		a.execute();
	}
}

public class A1 {
	public void execute(){
		
	}
}

public class B {
	private final IB a;

	public B(IB a) {
		this.a = a;
	}

	public void execute() {
		a.execute();
	}
}
public class B1 implements IB {
	public void execute(){
		
	}
}
public class C {
	private final IC a;

	public C(IC a) {
		this.a = a;
	}

	public void execute() {
		a.execute();
	}
}
public class C1 implements IC{
	public void execute() {
	}
}
public class C2 implements IC{
	public void execute() {
	}

}

テストコード

public class PolyPerformance {

	private static final int LOOP = 1000 * 1000 * 100;

	public static void main(String[] args) {

		System.out.println("No.\tLpCost\tA\tB\tC \t[ms.]");
	    for (int j = 0; j < 10; j++) {

	      System.out.print(j + 1 + "\t");
		  System.out.flush();
		      
	      long time = System.currentTimeMillis();
	      for (int i = 0; i < LOOP; i++) {
	        //loop cost
	      }
	      time = System.currentTimeMillis() - time;
	      System.out.print(time + "\t");
	      System.out.flush();

	      A a = new A(new A1());
	      time = System.currentTimeMillis();
	      for (int i = 0; i < LOOP; i++) {
	        a.execute();
	      }
	      time = System.currentTimeMillis() - time;
	      System.out.print(time + "\t");
	      System.out.flush();

	      B b = new B(new B1());
	      time = System.currentTimeMillis();
	      for (int i = 0; i < LOOP; i++) {
	        b.execute();
	      }
	      time = System.currentTimeMillis() - time;
	      System.out.print(time + "\t");
	      System.out.flush();

	      C c = new C(new C1());
	      C c2 = new C(new C2()); //どこも使用していないインスタンス。※ココをコメントアウトするだけでパフォーマンスが向上する。
	      time = System.currentTimeMillis();
	      for (int i = 0; i < LOOP; i++) {
	        c.execute();
	      }
	      time = System.currentTimeMillis() - time;
	      System.out.println(time + "\t");

	    }

	}

}

パフォーマンス結果

コメント前
No.	LpCost	A	B	C 	[ms.]
1	178	915	887	2305	
2	160	856	836	2209	
3	157	821	839	2231	
4	159	829	877	2230	
5	158	832	840	2224	
6	158	821	840	2203	
7	157	822	840	2221	
8	159	834	887	2264	
9	158	832	884	3588	
10	414	1670	847	2212	

コメント後
No.	LpCost	A	B	C 	[ms.]
1	164	870	877	846	
2	158	825	840	845	
3	157	757	850	789	
4	158	824	854	784	
5	157	756	843	789	
6	156	823	840	846	
7	156	753	847	786	
8	157	820	840	791	
9	157	823	837	843	
10	159	768	845	791	

バイトコード

コメント前
  public static void main(java.lang.String[] args);
      0  getstatic java.lang.System.out : java.io.PrintStream [20]
      3  ldc <String "No.\tLpCost\tA\tB\tC"> [26]
      5  invokevirtual java.io.PrintStream.println(java.lang.String) : void [28]
      8  iconst_0
      9  istore_1 [j]
     10  goto 350
     13  getstatic java.lang.System.out : java.io.PrintStream [20]
     16  new java.lang.StringBuilder [34]
     19  dup
     20  iload_1 [j]
     21  iconst_1
     22  iadd
     23  invokestatic java.lang.String.valueOf(int) : java.lang.String [36]
     26  invokespecial java.lang.StringBuilder(java.lang.String) [42]
     29  ldc <String "\t"> [44]
     31  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [46]
     34  invokevirtual java.lang.StringBuilder.toString() : java.lang.String [50]
     37  invokevirtual java.io.PrintStream.print(java.lang.String) : void [54]
     40  getstatic java.lang.System.out : java.io.PrintStream [20]
     43  invokevirtual java.io.PrintStream.flush() : void [57]
     46  invokestatic java.lang.System.currentTimeMillis() : long [60]
     49  lstore_2 [time]
     50  iconst_0
     51  istore 4 [i]
     53  goto 59
     56  iinc 4 1 [i]
     59  iload 4 [i]
     61  ldc <Integer 100000000> [8]
     63  if_icmplt 56
     66  invokestatic java.lang.System.currentTimeMillis() : long [60]
     69  lload_2 [time]
     70  lsub
     71  lstore_2 [time]
     72  getstatic java.lang.System.out : java.io.PrintStream [20]
     75  new java.lang.StringBuilder [34]
     78  dup
     79  lload_2 [time]
     80  invokestatic java.lang.String.valueOf(long) : java.lang.String [64]
     83  invokespecial java.lang.StringBuilder(java.lang.String) [42]
     86  ldc <String "\t"> [44]
     88  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [46]
     91  invokevirtual java.lang.StringBuilder.toString() : java.lang.String [50]
     94  invokevirtual java.io.PrintStream.print(java.lang.String) : void [54]
     97  getstatic java.lang.System.out : java.io.PrintStream [20]
    100  invokevirtual java.io.PrintStream.flush() : void [57]
    103  new A [67]
    106  dup
    107  new A1 [69]
    110  dup
    111  invokespecial A1() [71]
    114  invokespecial A(A1) [72]
    117  astore 4 [a]
    119  invokestatic java.lang.System.currentTimeMillis() : long [60]
    122  lstore_2 [time]
    123  iconst_0
    124  istore 5 [i]
    126  goto 137
    129  aload 4 [a]
    131  invokevirtual A.execute() : void [75]
    134  iinc 5 1 [i]
    137  iload 5 [i]
    139  ldc <Integer 100000000> [8]
    141  if_icmplt 129
    144  invokestatic java.lang.System.currentTimeMillis() : long [60]
    147  lload_2 [time]
    148  lsub
    149  lstore_2 [time]
    150  getstatic java.lang.System.out : java.io.PrintStream [20]
    153  new java.lang.StringBuilder [34]
    156  dup
    157  lload_2 [time]
    158  invokestatic java.lang.String.valueOf(long) : java.lang.String [64]
    161  invokespecial java.lang.StringBuilder(java.lang.String) [42]
    164  ldc <String "\t"> [44]
    166  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [46]
    169  invokevirtual java.lang.StringBuilder.toString() : java.lang.String [50]
    172  invokevirtual java.io.PrintStream.print(java.lang.String) : void [54]
    175  getstatic java.lang.System.out : java.io.PrintStream [20]
    178  invokevirtual java.io.PrintStream.flush() : void [57]
    181  new B [78]
    184  dup
    185  new B1 [80]
    188  dup
    189  invokespecial B1() [82]
    192  invokespecial B(IB) [83]
    195  astore 5 [b]
    197  invokestatic java.lang.System.currentTimeMillis() : long [60]
    200  lstore_2 [time]
    201  iconst_0
    202  istore 6 [i]
    204  goto 215
    207  aload 5 [b]
    209  invokevirtual B.execute() : void [86]
    212  iinc 6 1 [i]
    215  iload 6 [i]
    217  ldc <Integer 100000000> [8]
    219  if_icmplt 207
    222  invokestatic java.lang.System.currentTimeMillis() : long [60]
    225  lload_2 [time]
    226  lsub
    227  lstore_2 [time]
    228  getstatic java.lang.System.out : java.io.PrintStream [20]
    231  new java.lang.StringBuilder [34]
    234  dup
    235  lload_2 [time]
    236  invokestatic java.lang.String.valueOf(long) : java.lang.String [64]
    239  invokespecial java.lang.StringBuilder(java.lang.String) [42]
    242  ldc <String "\t"> [44]
    244  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [46]
    247  invokevirtual java.lang.StringBuilder.toString() : java.lang.String [50]
    250  invokevirtual java.io.PrintStream.print(java.lang.String) : void [54]
    253  getstatic java.lang.System.out : java.io.PrintStream [20]
    256  invokevirtual java.io.PrintStream.flush() : void [57]
    259  new C [87]
    262  dup
    263  new C1 [89]
    266  dup
    267  invokespecial C1() [91]
    270  invokespecial C(IC) [92]
    273  astore 6 [c]
    275  new C [87]
    278  dup
    279  new C2 [95]
    282  dup
    283  invokespecial C2() [97]
    286  invokespecial C(IC) [92]
    289  astore 7 [c2]
    291  invokestatic java.lang.System.currentTimeMillis() : long [60]
    294  lstore_2 [time]
    295  iconst_0
    296  istore 8 [i]
    298  goto 309
    301  aload 6 [c]
    303  invokevirtual C.execute() : void [98]
    306  iinc 8 1 [i]
    309  iload 8 [i]
    311  ldc <Integer 100000000> [8]
    313  if_icmplt 301
    316  invokestatic java.lang.System.currentTimeMillis() : long [60]
    319  lload_2 [time]
    320  lsub
    321  lstore_2 [time]
    322  getstatic java.lang.System.out : java.io.PrintStream [20]
    325  new java.lang.StringBuilder [34]
    328  dup
    329  lload_2 [time]
    330  invokestatic java.lang.String.valueOf(long) : java.lang.String [64]
    333  invokespecial java.lang.StringBuilder(java.lang.String) [42]
    336  ldc <String "\t"> [44]
    338  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [46]
    341  invokevirtual java.lang.StringBuilder.toString() : java.lang.String [50]
    344  invokevirtual java.io.PrintStream.println(java.lang.String) : void [28]
    347  iinc 1 1 [j]
    350  iload_1 [j]
    351  bipush 10
    353  if_icmplt 13
    356  return
    
    
    
    
    
    
    
    
    
    
    
    
 コメント後   
    public static void main(java.lang.String[] args);
      0  getstatic java.lang.System.out : java.io.PrintStream [20]
      3  ldc <String "No.\tLpCost\tA\tB\tC"> [26]
      5  invokevirtual java.io.PrintStream.println(java.lang.String) : void [28]
      8  iconst_0
      9  istore_1 [j]
     10  goto 334
     13  getstatic java.lang.System.out : java.io.PrintStream [20]
     16  new java.lang.StringBuilder [34]
     19  dup
     20  iload_1 [j]
     21  iconst_1
     22  iadd
     23  invokestatic java.lang.String.valueOf(int) : java.lang.String [36]
     26  invokespecial java.lang.StringBuilder(java.lang.String) [42]
     29  ldc <String "\t"> [44]
     31  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [46]
     34  invokevirtual java.lang.StringBuilder.toString() : java.lang.String [50]
     37  invokevirtual java.io.PrintStream.print(java.lang.String) : void [54]
     40  getstatic java.lang.System.out : java.io.PrintStream [20]
     43  invokevirtual java.io.PrintStream.flush() : void [57]
     46  invokestatic java.lang.System.currentTimeMillis() : long [60]
     49  lstore_2 [time]
     50  iconst_0
     51  istore 4 [i]
     53  goto 59
     56  iinc 4 1 [i]
     59  iload 4 [i]
     61  ldc <Integer 100000000> [8]
     63  if_icmplt 56
     66  invokestatic java.lang.System.currentTimeMillis() : long [60]
     69  lload_2 [time]
     70  lsub
     71  lstore_2 [time]
     72  getstatic java.lang.System.out : java.io.PrintStream [20]
     75  new java.lang.StringBuilder [34]
     78  dup
     79  lload_2 [time]
     80  invokestatic java.lang.String.valueOf(long) : java.lang.String [64]
     83  invokespecial java.lang.StringBuilder(java.lang.String) [42]
     86  ldc <String "\t"> [44]
     88  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [46]
     91  invokevirtual java.lang.StringBuilder.toString() : java.lang.String [50]
     94  invokevirtual java.io.PrintStream.print(java.lang.String) : void [54]
     97  getstatic java.lang.System.out : java.io.PrintStream [20]
    100  invokevirtual java.io.PrintStream.flush() : void [57]
    103  new A [67]
    106  dup
    107  new A1 [69]
    110  dup
    111  invokespecial A1() [71]
    114  invokespecial A(A1) [72]
    117  astore 4 [a]
    119  invokestatic java.lang.System.currentTimeMillis() : long [60]
    122  lstore_2 [time]
    123  iconst_0
    124  istore 5 [i]
    126  goto 137
    129  aload 4 [a]
    131  invokevirtual A.execute() : void [75]
    134  iinc 5 1 [i]
    137  iload 5 [i]
    139  ldc <Integer 100000000> [8]
    141  if_icmplt 129
    144  invokestatic java.lang.System.currentTimeMillis() : long [60]
    147  lload_2 [time]
    148  lsub
    149  lstore_2 [time]
    150  getstatic java.lang.System.out : java.io.PrintStream [20]
    153  new java.lang.StringBuilder [34]
    156  dup
    157  lload_2 [time]
    158  invokestatic java.lang.String.valueOf(long) : java.lang.String [64]
    161  invokespecial java.lang.StringBuilder(java.lang.String) [42]
    164  ldc <String "\t"> [44]
    166  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [46]
    169  invokevirtual java.lang.StringBuilder.toString() : java.lang.String [50]
    172  invokevirtual java.io.PrintStream.print(java.lang.String) : void [54]
    175  getstatic java.lang.System.out : java.io.PrintStream [20]
    178  invokevirtual java.io.PrintStream.flush() : void [57]
    181  new B [78]
    184  dup
    185  new B1 [80]
    188  dup
    189  invokespecial B1() [82]
    192  invokespecial B(IB) [83]
    195  astore 5 [b]
    197  invokestatic java.lang.System.currentTimeMillis() : long [60]
    200  lstore_2 [time]
    201  iconst_0
    202  istore 6 [i]
    204  goto 215
    207  aload 5 [b]
    209  invokevirtual B.execute() : void [86]
    212  iinc 6 1 [i]
    215  iload 6 [i]
    217  ldc <Integer 100000000> [8]
    219  if_icmplt 207
    222  invokestatic java.lang.System.currentTimeMillis() : long [60]
    225  lload_2 [time]
    226  lsub
    227  lstore_2 [time]
    228  getstatic java.lang.System.out : java.io.PrintStream [20]
    231  new java.lang.StringBuilder [34]
    234  dup
    235  lload_2 [time]
    236  invokestatic java.lang.String.valueOf(long) : java.lang.String [64]
    239  invokespecial java.lang.StringBuilder(java.lang.String) [42]
    242  ldc <String "\t"> [44]
    244  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [46]
    247  invokevirtual java.lang.StringBuilder.toString() : java.lang.String [50]
    250  invokevirtual java.io.PrintStream.print(java.lang.String) : void [54]
    253  getstatic java.lang.System.out : java.io.PrintStream [20]
    256  invokevirtual java.io.PrintStream.flush() : void [57]
    259  new C [87]
    262  dup
    263  new C1 [89]
    266  dup
    267  invokespecial C1() [91]
    270  invokespecial C(IC) [92]
    273  astore 6 [c]
    275  invokestatic java.lang.System.currentTimeMillis() : long [60]
    278  lstore_2 [time]
    279  iconst_0
    280  istore 7 [i]
    282  goto 293
    285  aload 6 [c]
    287  invokevirtual C.execute() : void [95]
    290  iinc 7 1 [i]
    293  iload 7 [i]
    295  ldc <Integer 100000000> [8]
    297  if_icmplt 285
    300  invokestatic java.lang.System.currentTimeMillis() : long [60]
    303  lload_2 [time]
    304  lsub
    305  lstore_2 [time]
    306  getstatic java.lang.System.out : java.io.PrintStream [20]
    309  new java.lang.StringBuilder [34]
    312  dup
    313  lload_2 [time]
    314  invokestatic java.lang.String.valueOf(long) : java.lang.String [64]
    317  invokespecial java.lang.StringBuilder(java.lang.String) [42]
    320  ldc <String "\t"> [44]
    322  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [46]
    325  invokevirtual java.lang.StringBuilder.toString() : java.lang.String [50]
    328  invokevirtual java.io.PrintStream.println(java.lang.String) : void [28]
    331  iinc 1 1 [j]
    334  iload_1 [j]
    335  bipush 10
    337  if_icmplt 13
    340  return