Performance Java-重复的函数调用减少了执行时间

Performance Java-重复的函数调用减少了执行时间,performance,jvm,java-8,java-stream,Performance,Jvm,Java 8,Java Stream,我有以下代码 public class BenchMark { public static void main(String args[]) { doLinear(); doLinear(); doLinear(); doLinear(); } private static void doParallel() { IntStream range = IntStream.range(

我有以下代码

public class BenchMark {
    public static void main(String args[]) {
        doLinear();

        doLinear();

        doLinear();

        doLinear();

    }


    private static void doParallel() {
        IntStream range = IntStream.range(1, 6).parallel();

        long startTime = System.nanoTime();
        int reduce = range
                .reduce((a, item) -> a * item).getAsInt();
        long endTime = System.nanoTime();
        System.out.println("parallel: " +reduce + " -- Time: " + (endTime - startTime));
    }

    private static void doLinear() {
        IntStream range = IntStream.range(1, 6);

        long startTime = System.nanoTime();
        int reduce = range
                .reduce((a, item) -> a * item).getAsInt();
        long endTime = System.nanoTime();
        System.out.println("linear: " +reduce + " -- Time: " + (endTime - startTime));
    }

}
我试图对流进行基准测试,但在一次又一次调用同一个函数时,执行时间稳步减少

输出:

linear: 120 -- Time: 57008226
linear: 120 -- Time: 23202
linear: 120 -- Time: 17192
linear: 120 -- Time: 17802

Process finished with exit code 0
第一次和第二次执行时间之间存在巨大差异

我相信JVM可能在幕后做了一些把戏,但有人能帮我理解那里到底发生了什么吗


是否有任何方法可以避免这种优化,以便我可以基准测试真正的执行时间?

更新:请参阅@Marko的答案,了解lambda链接导致的初始延迟的解释


第一次调用的执行时间较长可能是由于错误。简而言之,在第一次调用方法时,会将字节码JIT编译为本机代码。JVM然后尝试通过识别频繁调用的(热)方法进行进一步优化,并重新生成它们的代码以获得更高的性能

是否有任何方法可以避免这种优化,以便我可以基准测试真正的执行时间

您当然可以通过排除前几个结果来解释JVM初始预热。然后在数万次迭代的循环中增加对方法的重复调用次数,并平均结果


<> P>有更多的选项,您可能需要考虑添加到您的执行中来帮助减少噪声,如本文中所讨论的。这也有一些很好的提示。

更新:关于lambda链接导致的初始延迟的解释,请参考@Marko的答案


第一次调用的执行时间较长可能是由于错误。简而言之,在第一次调用方法时,会将字节码JIT编译为本机代码。JVM然后尝试通过识别频繁调用的(热)方法进行进一步优化,并重新生成它们的代码以获得更高的性能

是否有任何方法可以避免这种优化,以便我可以基准测试真正的执行时间

您当然可以通过排除前几个结果来解释JVM初始预热。然后在数万次迭代的循环中增加对方法的重复调用次数,并平均结果


<> P>有更多的选项,您可能需要考虑添加到您的执行中来帮助减少噪声,如本文中所讨论的。这也有一些很好的提示。

JavaVM在第一次使用类时将类加载到内存中。
因此,第一次运行和第二次运行之间的差异可能是由类加载引起的。

JavaVM在第一次使用类时将类加载到内存中。 因此,第一次和第二次运行之间的差异可能是由类加载引起的

我相信JVM可能在幕后做了一些把戏,但有人能帮我理解那里到底发生了什么吗

  • 第一次调用的巨大延迟是由于整个lambda运行时子系统的初始化造成的。您只需为整个应用程序支付一次

  • 当您的代码第一次到达任何给定的lambda表达式时,您需要为该lambda的链接付费(初始化
    invokedynamic
    调用站点)

  • 经过一些迭代后,由于JIT编译器优化了简化代码,您将看到额外的加速

  • 是否有任何方法可以避免这种优化,以便我可以基准测试真正的执行时间

    您在这里提出了一个矛盾:“真正”的执行时间是您在预热后得到的时间,此时所有优化都已应用。这是实际应用程序将经历的运行时。前几次运行的延迟与更广泛的情况无关,除非您对单镜头性能感兴趣

    为了便于探索,您可以查看禁用JIT编译时代码的行为:将
    -Xint
    传递到
    java
    命令。还有更多的标志用于禁用优化的各个方面

    我相信JVM可能在幕后做了一些把戏,但有人能帮我理解那里到底发生了什么吗

  • 第一次调用的巨大延迟是由于整个lambda运行时子系统的初始化造成的。您只需为整个应用程序支付一次

  • 当您的代码第一次到达任何给定的lambda表达式时,您需要为该lambda的链接付费(初始化
    invokedynamic
    调用站点)

  • 经过一些迭代后,由于JIT编译器优化了简化代码,您将看到额外的加速

  • 是否有任何方法可以避免这种优化,以便我可以基准测试真正的执行时间

    您在这里提出了一个矛盾:“真正”的执行时间是您在预热后得到的时间,此时所有优化都已应用。这是实际应用程序将经历的运行时。前几次运行的延迟与更广泛的情况无关,除非您对单镜头性能感兴趣

    为了便于探索,您可以查看禁用JIT编译时代码的行为:将
    -Xint
    传递到
    java
    命令。还有更多的标志用于禁用优化的各个方面

    真实执行时间

    没有“真正的执行时间”这样的东西。如果您只需要解决此任务一次,那么真正的执行时间将是第一次测试的时间(以及启动JVM本身的时间)。通常,执行给定代码所花费的时间取决于许多因素:

    • 无论这段代码是解释的,JIT都是由C1或C2编译器编译的。请注意,不仅仅有三个选项。如果从另一个方法调用一个方法,其中一个可能会被解释,另一个可能会被编译

    • 对于C2编译器:这段代码以前是如何执行的,那么分支和类型概要文件中有什么内容呢。波尔