【问题标题】:How are JVM optimizations based on assumptions?JVM 优化如何基于假设?
【发布时间】:2019-07-29 20:39:51
【问题描述】:

Java Concurrency In Practice 一书在第 12.3.3 节“不切实际的代码路径采样”中说:

在某些情况下,JVM 可能会根据可能只是暂时正确的假设进行优化,然后如果它们变得不正确,则通过使编译的代码无效来撤销它们

我无法理解上面的陈述。

  1. 这些 JVM 假设是什么?
  2. JVM 如何知道假设是真的还是假的?
  3. 如果假设不正确,是否会影响我的数据的正确性?

【问题讨论】:

  • 1) 例如,branch prediction。 2) 追溯检查,如果错误则回滚 3) 否
  • @Michael 分支预测与 JVM 无关。它发生在底层硬件中。
  • (3) 如果 JVM 在你的应用程序中插入错误,那会非常粗鲁,不是吗?
  • @Marco13 JVM 肯定会观察分支行为并进行相应优化。调用“分支预测”可能对我来说是不正确的——我现在似乎已经搜索过该术语通常只适用于 CPU 行为——但原理是一样的。
  • @Michael 在 JIT 编译的不同层级中有很多黑魔法。有些决定是基于分析信息,但我不知道那里使用了哪些信息,以及如何。我从未听说过可以称为“JVM 级别分支预测”的优化,但 可能 JVM 在某些时候会这样做。

标签: java jvm jit


【解决方案1】:

你引用的语句有一个脚注,它给出了一个例子:

例如,如果当前加载的类没有覆盖该方法,JVM 可以使用单态调用转换将虚拟方法调用转换为直接方法调用,但如果类是随后加载覆盖该方法。

这里的细节非常,非常非常复杂。所以下面是一个极其简单化的例子。

假设你有一个界面:

 interface Adder { int add(int x); }

该方法应该向x 添加一个值,并返回结果。现在想象有一个程序使用这个类的实现:

class OneAdder implements Adder { 
    int add(int x) {
        return x+1;
    }
}

class Example {

    void run() {
        OneAdder a1 = new OneAdder();
        int result = compute(a1);
        System.out.println(result);
    }

    private int compute(Adder a) {
        int sum = 0;
        for (int i=0; i<100; i++) {
            sum = a.add(sum);
        }
        return sum;
    }
}

在此示例中,JVM 可以进行某些优化。一个非常低级的方法是它可以避免使用vtable 来调用add 方法,因为在给定的程序中该方法只有一个实现。但它甚至可以更进一步,inline 这个唯一的方法,所以compute 方法本质上变成了这样:

private int compute(Adder a) {
    int sum = 0;
    for (int i=0; i<100; i++) {
        sum += 1;
    }
    return sum;
}

原则上,即使是这样

private int compute(Adder a) {
    return 100;
}

但是 JVM 也可以在运行时加载类。所以可能会有这种优化已经完成的情况,稍后,JVM会像这样加载一个类:

class TwoAdder implements Adder { 
    int add(int x) {
        return x+2;
    }
}

现在,对compute 方法所做的优化可能会变为“无效”,因为不清楚是用OneAdder 还是TwoAdder 调用它。在这种情况下,必须撤消优化。

这应该回答了您的1.个问题。

关于2.:当然,JVM 会跟踪所有已完成的优化。它知道它已经内联了add 方法,前提是该方法只有一个 实现。当它找到该方法的另一个实现时,它必须撤消优化。

关于3.:当假设为真时进行优化。当它们变得不真实时,优化将被撤消。所以这不会影响你程序的正确性


更新:

同样,上面的示例非常简化,参考了书中给出的脚注。更多关于JVM优化技术的信息,可以参考https://wiki.openjdk.java.net/display/HotSpot/PerformanceTechniques。具体来说,speculative (profile-based) techniques 可能被认为主要基于“假设”——即,基于迄今为止收集的分析数据做出的假设。

【讨论】:

  • 哇。很好的答案!
【解决方案2】:

结合上下文引用的文本,本书的这一部分实际上是在讨论在进行性能测试时使用真实文本数据(输入)的重要性。

您的问题:


这些 JVM 假设是什么?

我认为文中讲了两件事:

  • 一方面,它似乎在谈论基于代码路径测量的优化。例如,if 语句的“then”或“else”分支是否更有可能被执行。这确实会导致生成不同的代码,并且如果初始测量不正确,很容易产生次优代码。

  • 另一方面,它似乎也在谈论可能被证明无效的优化。例如,在某个时间点,可能只有一个给定接口方法的实现已被 JVM 加载。看到这一点,优化器可能决定简化调用序列以避免多态方法分派。 (书中使用的术语是“单态调用转换”。)稍晚一点,可能会加载第二个实现,导致优化器退出该优化。

    李>

第一种情况只会影响性能。

如果优化器没有取消优化,第二个会影响正确性(以及性能)。但是优化器确实这样做了。所以它只影响性能。 (包含受影响调用的方法需要重新优化,这会影响整体性能。)

JVM 如何知道假设是真的还是假的?

在第一种情况下,它没有。

在第二种情况下,当 JVM 加载第二个方法时注意到问题,并在(比如说)接口方法上看到一个标志,表明优化器假定它实际上是一个最终方法。看到这一点,装载机在造成任何损坏之前触发“退出”。

如果假设不正确,是否会影响我的数据的正确性?

不,它没有。两种情况都不是。


但本节的要点是,您的测试数据的性质会影响性能测量。这不仅仅是大小的问题。测试数据还需要使应用程序的行为方式与“现实生活”中的行为方式相同(采用相似的代码路径)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-10-15
    • 1970-01-01
    • 2013-10-28
    • 2011-09-06
    • 1970-01-01
    • 2013-03-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多