在这段代码中,当我在 main
方法中创建一个对象,然后调用该对象方法: ff.twentyDivCount(i)
(runs in 16010 ms) 时,它运行速度比使用以下注释调用它快得多:twentyDivCount(i)
(运行时间为 59516 毫秒)。当然,当我在不创建对象的情况下运行它时,我将方法设为静态,因此可以在 main 中调用它。
public class ProblemFive {
// Counts the number of numbers that the entry is evenly divisible by, as max is 20
int twentyDivCount(int a) { // Change to static int.... when using it directly
int count = 0;
for (int i = 1; i<21; i++) {
if (a % i == 0) {
count++;
}
}
return count;
}
public static void main(String[] args) {
long startT = System.currentTimeMillis();;
int start = 500000000;
int result = start;
ProblemFive ff = new ProblemFive();
for (int i = start; i > 0; i--) {
int temp = ff.twentyDivCount(i); // Faster way
// twentyDivCount(i) - slower
if (temp == 20) {
result = i;
System.out.println(result);
}
}
System.out.println(result);
long end = System.currentTimeMillis();;
System.out.println((end - startT) + " ms");
}
}
编辑:到目前为止,似乎不同的机器会产生不同的结果,但使用 JRE 1.8.* 似乎可以始终如一地重现原始结果。
最佳答案
使用 JRE 1.8.0_45 我得到了类似的结果。
调查:
- 使用
-XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation -XX:+PrintInlining
VM 选项运行 java 表明这两种方法都已编译和内联 - 查看为方法本身生成的程序集没有显着差异
- 然而,一旦它们被内联,在
main
中生成的程序集就大不相同了,实例方法被更积极地优化,尤其是在循环展开方面
然后我再次运行您的测试,但使用不同的循环展开设置来确认上述怀疑。我运行了你的代码:
-XX:LoopUnrollLimit=0
并且这两个方法运行缓慢(类似于带有默认选项的静态方法)。-XX:LoopUnrollLimit=100
并且两个方法都运行得很快(类似于带有默认选项的实例方法)。
作为一个结论,似乎在默认设置下,热点 1.8.0_45 的 JIT 在方法为静态时无法展开循环(虽然我不确定为什么会这样行为方式)。其他 JVM 可能会产生不同的结果。
关于java - 等效静态和非静态方法的速度差异很大,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30454904/