gpt4 book ai didi

java - 当JVM不执行方法时进行代码优化

转载 作者:行者123 更新时间:2023-12-02 04:45:32 34 4
gpt4 key购买 nike

我正在阅读 Scott Oaks 的《Java 性能》一书,遇到了一段代码,据说 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;
}

书中的进一步陈述:由于斐波那契计算的结果从未被使用,因此编译器可以随意丢弃该计算。智能编译器(包括当前的 Java 7 和 8 编译器)最终将执行以下代码:

long then = System.currentTimeMillis();
long now = System.currentTimeMillis();
System.out.println("Elapsed time: " + (now - then));

为了验证这一点,我尝试了一个代码,但耗时计算并没有反射(reflect)上面解释的理论。

我的代码版本:

    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 正在做什么,那么正确地执行它确实很复杂。死代码消除 (DCE) 是一种经常导致微基准测试出错的优化。

有两个经典错误(详细了解常见错误 http://shipilev.net/talks/jvmls-July2014-benchmarking.pdfhttps://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:+PrintInlined -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 compilation在基准测试(尤其是微基准测试)中频繁触发,在应用程序代码中触发频率较低。为了获得正确的结果,我们必须添加更多的预热迭代:

    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

关于java - 当JVM不执行方法时进行代码优化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29692179/

34 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com