継承によるパフォーマンス劣化の落とし穴
あるインターフェースを実装している異なる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