随分昔の記事を取り出して恐縮ですが、finalをつけたらパフォーマンスが落ちたでござるの巻 という話がありました。
この違いがなぜ起こるか調べてみました。
public static void main(String[] args) {
final int NUM = 31;
long start = System.currentTimeMillis();
for (int i = 0; i < Integer.MAX_VALUE; i++) {
int hash = 17;
hash = hash * NUM + i;
hash = hash * NUM + i;
hash = hash * NUM + i;
hash = hash * NUM + i;
hash = hash * NUM + i;
}
System.out.println(System.currentTimeMillis() - start);
}
この場合、javapの結果を眺めてみると、違いはが出てくるのは、finalを付けるとスタックに積むのがbipush
命令になり、finalを付けないとiload_1
命令になるという点だけです。
これをWindowsで実行すると…
finalあり:3244
finalなし:2527
確かにfinalなしの方が速い結果が得られます。
しかしこのコードよく見ると、hashがループの外で使われていないので、最適化のやり方次第で大きく差がでてしまいます。-XintをつけてJITを効かせずに実行してみます。
finalあり:57513
finalなし:57441
変わりません。つまりbipushとiload_1で速度差は無いということです。
さて比較をフェアにするために、hashをループの外で使うようにして、-serverオプションをつけて最適化して実行してみます。
コードは以下のとおり。
public static void main(String[] args) {
final int NUM = 31;
long start = System.currentTimeMillis();
int hash = 17;
for (int i = 0; i < Integer.MAX_VALUE; i++) {
hash = hash * NUM + i;
hash = hash * NUM + i;
hash = hash * NUM + i;
hash = hash * NUM + i;
hash = hash * NUM + i;
}
System.out.println(hash +":"+ (System.currentTimeMillis() - start));
}
結果は…
finalあり:9021
finalなし:12478
となり、「finalを付けた方が、最適化されやすい」という通説どおりの結果になります。