[英]When JVM does not execute method for code optimization
我正在浏览Scott Oaks的《 Java Performance》一书时,遇到了一个代码,据说Java 7或8 JVM足够聪明,可以跳过for循环中提供的计算部分,因为将来不使用结果(微基准测试)。
书中提到的代码:
public void doTest() {
// Main Loop
double l;
long then = System.currentTimeMillis();
for (int i = 0; i < nLoops; i++) {
l = fibImpl1(50);
}
long now = System.currentTimeMillis();
System.out.println("Elapsed time: " + (now - then));
}
private double fibImpl1(int n) {
if (n < 0) throw new IllegalArgumentException("Must be > 0");
if (n == 0) return 0d;
if (n == 1) return 1d;
double d = fibImpl1(n - 2) + fibImpl(n - 1);
if (Double.isInfinite(d)) throw new ArithmeticException("Overflow");
return d;
}
书中的进一步说明:由于从未使用过Fibonacci计算的结果,因此编译器可以随意放弃该计算。 一个智能编译器(包括当前的Java 7和8编译器)将最终执行以下代码:
long then = System.currentTimeMillis();
long now = System.currentTimeMillis();
System.out.println("Elapsed time: " + (now - then));
为了验证同样的结果,我尝试了一个代码,但是经过时间的计算并不能反映上述理论。
我的代码版本:
public class APSum {
public static void main(String[] args) {
long then = System.currentTimeMillis();
ArithmeticProgression.sum(500000000);
long now = System.currentTimeMillis();
System.out.println("Elapsed time: " + (now - then));
}
}
class ArithmeticProgression{
public static double sum(int i){
double sum=0;
for(int index=0; index<=i; index++){
sum = sum + (double)index;
}
return sum;
}
}
因此,请让我知道如何实现书中提到的方案。 还是JVM希望JVM是否要优化调用?
现代JVM太复杂了,并且会进行各种优化。 如果尝试测量一些小的代码,那么在没有非常非常详细的JVM工作知识的情况下,正确地进行操作确实很复杂。 死代码消除(DCE)是一种经常导致微基准出错的优化方法。
有两个经典错误(有关常见错误的更多信息, 请参见http://shipilev.net/talks/jvmls-July2014-benchmarking.pdf和https://stackoverflow.com/a/513259/1352098 ):
校正后,我们的基准如下所示:
public class APSum {
public static void main(String[] args) {
for (int i = 0; i < 5000; i++) {
test();
}
}
private static void test() {
long then = System.currentTimeMillis();
sum(5000000);
long now = System.currentTimeMillis();
System.out.println("Elapsed time: " + (now - then));
}
public static double sum(int i){
double sum=0;
for(int index=0; index<=i; index++){
sum = sum + (double)index;
}
return sum;
}
}
在此示例中,DCE仅在内联之后发生。 让我们从内联树开始看(可用于-XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining -XX:-BackgroundCompilation
)
@ 0 java.lang.System::currentTimeMillis (0 bytes) intrinsic
@ 6 edu.jvm.runtime.APSum::sum (22 bytes) inlining prohibited by policy
@ 10 java.lang.System::currentTimeMillis (0 bytes) intrinsic
由于OSR,编译器无法内联方法APSum :: sum。 实际上,尽管OSR编译在基准测试中(尤其是在微基准测试中)经常被触发,但是在应用程序代码中却很少被触发。 为了获得正确的结果,我们必须添加更多的预热迭代:
public class APSum {
public static void main(String[] args) {
for (int i = 0; i < 10000; i++) {
test();
}
}
private static void test() {
long then = System.currentTimeMillis();
sum(5000000);
long now = System.currentTimeMillis();
System.out.println("Elapsed time: " + (now - then));
}
public static double sum(int i){
double sum=0;
for(int index=0; index<=i; index++){
sum = sum + (double)index;
}
return sum;
}
}
结果,在迭代结束时,我们得到:
Elapsed time: 5
Elapsed time: 4
Elapsed time: 5
@ 0 java.lang.System::currentTimeMillis (0 bytes) (intrinsic)
@ 6 edu.jvm.runtime.APSum::sum (22 bytes) inline (hot)
@ 10 java.lang.System::currentTimeMillis (0 bytes) (intrinsic)
Elapsed time: 0
Elapsed time: 0
Elapsed time: 0
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.