JVM 优化是如何基于假设的?

How are JVM optimizations based on assumptions?

在第 12.3.3 节中,"Unrealistic Sampling of Code Paths" Java 并发实践 一书说:

In some cases, the JVM may make optimizations based on assumptions that may only be true temporarily, and later back them out by invalidating the compiled code if they become untrue

我无法理解上面的说法。

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

你引用的声明有一个脚注,其中给出了一个例子:

For example, the JVM can use monomorphic call transformation to convert a virtual method call to a direct method call if no classes currently loaded override that method, but it invalidates the compiled code if a class is subsequently loaded that overrides the method.

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

假设你有一个接口:

 interface Adder { int add(int x); }

该方法应该将一个值添加到 x,并且 return 结果。现在假设有一个程序使用这个 class:

的实现
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 方法,因为在给定的程序中只有这个方法的一个实现。但它甚至可以走得更远,内联这唯一的方法,所以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 也可以在运行时加载 classes。所以可能会有这样的情况,这个优化已经完成了,之后,JVM加载一个class是这样的:

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

现在对compute方法做的优化可能会变成"invalid",因为不清楚是用OneAdder调用还是用[=20=调用].在这种情况下,必须取消优化。

这应该回答 1. 你的问题。

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

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


更新:

同样,上面的例子非常简单,参考了书中给出的脚注。关于 JVM 的优化技术的更多信息,您可以参考 https://wiki.openjdk.java.net/display/HotSpot/PerformanceTechniques . Specifically, the speculative (profile-based) techniques 可能被认为主要基于 "assumptions" - 即基于已被分析的分析数据所做的假设收集到此为止。

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

您的问题:


What are these JVM assumptions?

我觉得文中讲的是两件事:

  • 一方面,好像是在讲基于代码路径度量的优化。例如,if 语句的 "then" 或 "else" 分支更有可能被执行。这确实会导致生成不同的代码,并且如果初始测量不正确,则容易生成次优代码。

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

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

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

How do JVM know the assumptions are true or untrue?

在第一种情况下,它不会。

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

If the assumptions are untrue, does it influence the correctness of my data?

不,不是。两种情况都不是。


但是本节的要点是测试数据的性质会影响性能测量。这不仅仅是一个大小问题。测试数据还需要使应用程序的行为方式与 "real life".

中的行为方式相同(采用类似的代码路径)