java - 调试 (i!=t++) 与 (t++!=i)

标签 java debugging

我正在解决一些必须使用 (i!=t++) 条件的问题。不幸的是,使用这种情况导致了 TLE。但是当我使用 (t++!=i) 而不是它时,它已成功提交。我认为这两个条件对于下面的程序是相同的。程序在我的编译器上的输出是 5259 3分别是第一次和第二次循环所花费的时间 我找不到这两种情况之间的区别。当然在某个地方有我找不到的错误

void test()
{
    long b=System.currentTimeMillis();
    for(int i=0;i<100001;i++) {
        int t=0;
        while (i!=t++)
        {
            int x=34;
            x++;

        }
    }
    System.out.println(System.currentTimeMillis()-b);

    b=System.currentTimeMillis();
    for(int i=0;i<100001;i++) {
        int t=0;
        while (t++!=i)
        {
            int x=34;
            x--;
            x++;
        }
    }
}

最佳答案

这是一个部分答案,需要进一步调查。但是,就目前而言,答案是 - 这是 JIT 优化的效果。另请注意,微基准测试并不是性能测试的最佳选择,尤其是对于动态编译语言(如 Java)(例如,参见 this guru paper)。

我正在使用 Windows 10 家庭版,java -version 打印:

java version "1.8.0_121"
Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)

我已经按如下方式重组了您的代码并添加了 x 作为外部计数器以确保循环不会被优化掉:

void test1() {
    int x = 0;
    long b = System.currentTimeMillis();
    for (int i = 0; i < 100_001; i++) {
        int t = 0;
        while (i != t++) {
            x++;
        }
    }
    long b1 = System.currentTimeMillis();
    System.out.println("T(test1) = " + (b1 - b));
    System.out.println("x(test1) = " + x);
}

void test2() {
    int x=0;
    long b = System.currentTimeMillis();
    for (int i = 0; i < 100001; i++) {
        int t = 0;
        while (t++ != i) {
            x++;
        }
    }
    long b1 = System.currentTimeMillis();
    System.out.println("T(test2) = " + (b1 - b));
    System.out.println("x(test2) = " + x);
}

每个函数被调用两次:

t.test1();
t.test1();
t.test2();
t.test2();

好的,让我们看看标准 java Test 调用的结果(没有提供其他的 interpeter 参数):

T(test1) = 3745
x(test1) = 705082704
T(test1) = 0
x(test1) = 705082704
T(test2) = 5
x(test2) = 705082704
T(test2) = 0
x(test2) = 705082704

如您所见,在第二次调用之后,两种情况下的运行时间都为 0。即使我们将 int x = 0 初始化更改为 int x = new Random().nextInt() 以确保不缓存第二次调用结果或其他内容,也会发生同样的情况.通常,应该在进行测量之前“预热”Java 解释器,即在同一次运行中测量代码性能两次并丢弃第一个结果,以便确保优化到位。但这是您在解决在线评委练习时所没有的奢侈。

现在是另一部分。 Oracle 的 JDK 有一个 -Xint 解释器开关,可以完全关闭 JIT。让我们使用它,看看会发生什么。我还使用了 -XX:+PrintCompilation 标志来确保没有发生任何编译(即解释器被调用为 java -XX:+PrintCompilation -Xint Test ; 如果没有打印额外的诊断信息,则表示代码未编译):

T(test1) = 56610
x(test1) = 705082704
T(test1) = 55635
x(test1) = 705082704
T(test2) = 60247
x(test2) = 705082704
T(test2) = 58249
x(test2) = 705082704

两个观察结果:现在任务需要很长时间,并且所有调用的结果都相似。必须进行更多调查才能发现 JIT 对这两个代码进行不同优化的原因。

编辑:有趣的 JIT 第 2 部分

所以,我继续尝试不同的编译选项。一般来说,JIT 使用两种类型的编译器。 C1(客户端)编译器旨在提供更快的 JVM 启动时间,但不如 C2(服务器)编译器快。我使用的 64 位 Java 8 JVM 似乎使服务器成为唯一可用的选项(请参阅 this FAQ ;但是仍然可以使用 -XX:TieredStopAtLevel= 标志选择不同的编译级别;为简洁起见,我不会粘贴我使用它得到的结果,但他们支持这样的论点,即服务器编译器版本使 test2 的第一次调用更快)。

我的机器上也恰好有 32 位 JRE。它不支持服务器编译器并提供以下版本信息:

java version "1.8.0_121"
Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
Java HotSpot(TM) Client VM (build 25.121-b13, mixed mode)

此 JVM 的结果如下:

T(test1) = 3947
x(test1) = 705082704
T(test1) = 3985
x(test1) = 705082704
T(test2) = 4031
x(test2) = 705082704
T(test2) = 4172
x(test2) = 705082704

关于java - 调试 (i!=t++) 与 (t++!=i),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43053777/

相关文章:

java - 使用Java访问windows-my时如何避免智能卡选择弹出窗口

.net - Visual Studio 2010断点未加载任何符号

.net - 调试windows服务

iphone - 如何对 jQuery Mobile iOS/Android Web 应用程序进行性能调优

java - 为什么一个地方的原始类型会导致其他地方的通用调用站点被视为原始类型?

java - 编写多个自定义 Jackson 反序列化器来处理继承的正确方法

java - 从 Java 内部处理 Python IO

debugging - 如何使用 SOS(或 SOSEX)在 WinDbg 的字段之一中显示具有特定值的托管对象?

c# - 调试时显示奇怪的对象成员

java - Android ScrollView 问题