java - 为什么这个相同的代码会变慢?

标签 java

我正在尝试测量执行此循环的时间:

for (boolean t : test) {
    if (!t)
        ++count;
}

并且得到不一致的结果。最终我设法通过以下代码获得了一致的结果:

public class Test {
    public static void main(String[] args) {
        int size = 100;
        boolean[] test = new boolean[10_000_000];
        java.util.Random r = new java.util.Random();
        for (int n = 0; n < 10_000_000; ++n)
            test[n] = !r.nextBoolean();

        int expected = 0;
        long acumulated = 0;
        for (int repeat = -1; repeat < size; ++repeat) {
            int count = 0;
            long start = System.currentTimeMillis();
            for (boolean t : test) {
                if (!t)
                    ++count;
            }
            long end = System.currentTimeMillis();
            if (repeat != -1)  // First run does not count, VM warming up
                acumulated += end - start;
            else                  // Use count to avoid compiler or JVM
                expected = count; //optimization of inner loop
            if ( count!=expected )
                throw new Error("Tests don't run same ammount of times");
        }
        float average = (float) acumulated / size;
        System.out.println("1st test : " + average);

        int expectedBis = 0;
        acumulated = 0;
        if ( "reassign".equals(args[0])) {
            for (int n = 0; n < 10_000_000; ++n)
                test[n] = test[n];
        }
        for (int repeat = -1; repeat < size; ++repeat) {
            int count = 0;
            long start = System.currentTimeMillis();
            for (boolean t : test) {
                if (!t)
                    ++count;
            }
            long end = System.currentTimeMillis();
            if (repeat != -1)  // First run does not count, VM warming up
                acumulated += end - start;
            else                     // Use count to avoid compiler or JVM
                expectedBis = count; //optimization of inner loop
            if ( count!=expected || count!=expectedBis)
                throw new Error("Tests don't run same ammount of times");
        }
        average = (float) acumulated / size;
        System.out.println("2nd test : " + average);
    }

}

我得到的结果是:

$ java -jar Test.jar noreassign
1st test : 23.98
2nd test : 23.97
$ java -jar Test.jar reassign
1st test : 23.98
2nd test : 40.86
$ java -version
java version "1.7.0_79"
OpenJDK Runtime Environment (IcedTea 2.5.5) (Gentoo package icedtea-7.2.5.5)
OpenJDK 64-Bit Server VM (build 24.79-b02, mixed mode)

区别在于在第二次测试之前是否执行此循环。

for (int n = 0; n < 10_000_000; ++n)
    test[n] = test[n];

为什么?为什么重新分配会导致这些循环花费两倍的时间?
正确进行分析很难...

最佳答案

"As for why the JIT compiler causes such behaviour... that is beyond my skill and knowledge."

三个基本事实:

  1. JIT 编译后代码运行速度更快。

  2. JIT 编译在一段代码运行一段时间后触发。 (“一点”的长度受 JVM 平台和命令行选项的影响。)

  3. JIT 编译需要时间。

在您的情况下,当您在测试 1 和测试 2 之间插入大分配循环时,您很可能将触发 JIT 编译的时间点......从测试 2 期间移动到 2 测试之间。

在这种情况下解决这个问题的简单方法是将 main 的主体放入一个循环中并重复运行它。然后丢弃前几次运行中的异常结果。

(关闭 JIT 编译不是一个好的答案。通常,JIT 编译后代码的性能特征将指示真实应用程序的性能...)

通过将编译器设置为 NONE,您将禁用 JIT 编译,将其排除在外。


当人们尝试手动编写微基准时,这种异常情况很常见。阅读此问答:

关于java - 为什么这个相同的代码会变慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32319932/

相关文章:

java - Junit 参数化的结果错误

java |将字符串保留为枚举

java - 重新附加未执行 SQL 查询的分离实体

java - 重启 Storm 时再次处理来自 Kafka 的所有预处理记录

java - JDO在Date中不存储时间信息,只存储日期

java - Android 上的 HTTP 基本授权网络问题

java - 我们有 .NET "mustinherit"或 "notinheritable"的 Java 对应者吗?

java - 如何设置程序的硬盘位置以供搜索或编辑? ( java )

java - 为什么 Dagger 注入(inject)具体类?

java - JTextArea 具有相同的参数但大小不同