即使认为这不是它的主要目的,我一直认为final
关键字(在某些情况下和VM实现中)可以帮助JIT。
这可能是一个城市传说,但我从未想过设置一个字段final
会对性能产生负面影响。
在我碰到这样的代码之前:
private static final int THRESHOLD = 10_000_000;
private static int [] myArray = new int [THRESHOLD];
public static void main(String... args) {
final long begin = System.currentTimeMillis();
//Playing with myArray
int index1,index2;
for(index1 = THRESHOLD - 1; index1 > 1; index1--)
myArray[index1] = 42; //Array initial data
for(index1 = THRESHOLD - 1; index1 > 1; index1--) {
//Filling the array
for(index2 = index1 << 1; index2 < THRESHOLD; index2 += index1)
myArray[index2] += 32;
}
long result = 0;
for(index1 = THRESHOLD - 1; index1 > 1; index1-=100)
result += myArray[index1];
//Stop playing, let's see how long it took
System.out.println(result);
System.out.println((System.currentTimeMillis())-begin+"ms");
}
我们来看看:
private static int [] myArray = new int [THRESHOLD];
在W7 64位下,基于10次连续运行,我得到以下结果:
THRESHOLD = 10^7
,1.7.0u09客户端VM(Oracle):
myArray
不是最终版时运行〜2133ms。myArray
为最终版时, THRESHOLD = 3x10^7
,1.7.0u09客户端VM(Oracle):
myArray
不是最终版时,myArray
为最终版时, THRESHOLD = 3x10^7
,1.7.0u01客户端VM(Oracle):
myArray
不是最终版时,myArray
为最终版时, 备注:我使用JDK 1.7.0u09的javac生成的字节码进行所有测试。除了myArray
声明之外,两个版本生成的字节码完全相同,这是预期的。
那么为什么static final myArray
的版本比static myArray
的版本慢?
编辑(使用Aubin版本的我的代码段):
看来,带有final
关键字的版本与不含关键字的版本之间的差异在于第一次迭代。不知何故,final
的版本总是慢于没有第一次迭代的版本,然后下一次迭代有相似的时间。
例如,使用THRESHOLD = 10^8
并使用1.7.0u09客户端运行时,第一次计算需要大约35秒,而第二次“仅”需要30秒。
显然VM执行了优化,是JIT在运行,为什么它没有提前启动(例如通过编译嵌套循环的第二级,这部分是热点)?
请注意,我的备注对1.7.0u01客户端VM仍然有效。使用那个版本(可能是早期版本), final myArray
的代码比没有此关键字的代码运行得慢:2671ms vs 2331ms,基于200次迭代。 < / p>
答案 0 :(得分:4)
恕我直言,不应添加System.out.println(结果)的时间,因为I / O是高度变量且耗时的。
我认为println()影响因素更大,比最终影响更大。
我建议编写性能测试如下:
public class Perf {
private static final int THRESHOLD = 10_000_000;
private static final int[] myArray = new int[THRESHOLD];
private static /* */ long min = Integer.MAX_VALUE;
private static /* */ long max = 0L;
private static /* */ long sum = 0L;
private static void perf( int iteration ) {
final long begin = System.currentTimeMillis();
int index1, index2;
for( index1 = THRESHOLD - 1; index1 > 1; index1-- ) {
myArray[ index1 ] = 42;
}
for( index1 = THRESHOLD - 1; index1 > 1; index1-- ) {
for( index2 = index1 << 1; index2 < THRESHOLD; index2 += index1 ) {
myArray[ index2 ] += 32;
}
}
long result = 0;
for( index1 = THRESHOLD - 1; index1 > 1; index1 -= 100 ) {
result += myArray[ index1 ];
}
if( iteration > 0 ) {
long delta = System.currentTimeMillis() - begin;
sum += delta;
min = Math.min( min, delta );
max = Math.max( max, delta );
System.out.println( iteration + ": " + result );
}
}
public static void main( String[] args ) {
for( int iteration = 0; iteration < 1000; ++iteration ) {
perf( iteration );
}
long average = sum / 999;// the first is ignored
System.out.println( "Min : " + min + " ms" );
System.out.println( "Average: " + average + " ms" );
System.out.println( "Max : " + max + " ms" );
}
}
只有10次迭代的结果是:
决赛:
Min : 7645 ms
Average: 7659 ms
Max : 7926 ms
没有最终结果:
Min : 7629 ms
Average: 7780 ms
Max : 7957 ms
我建议读者运行此测试并发布他们的结果进行比较。