1

在评估一些非常具体的 if 条件时,我遇到了一些非常奇怪的性能行为。

基本上,我发现我可以构造两个条件 a 和 b,这样即使总是 false 也if(a)快 10 倍,这似乎与 Java 应该对条件进行惰性求值这一事实直接冲突。if(a && b)a

这种情况发生在我必须a成为someDate.after(startdate)b成为someDate.before(enddate)——即相当标准的范围条件时。

下面我附上了我用来显示这个特定问题的代码。我已经在带有 Java 7 的 Windows 7 上运行了代码,我的一位同事已经在带有 Java 6,7 和 8 的 Windows 7 上运行了它——结果都一样。

谁能解释为什么会发生这种情况?

import java.util.Date;

public class DateWeirdness {
public static void main(String[] args) {
    Date start = new Date();
    System.out.println("if(a) - a always false");
    System.out.println(timeBasic(2000000000, start, start.getTime() - 4000000000L));
    start = new Date();
    System.out.println("if(a && b) - a always false, a and b both date method");
    System.out.println(timeAdv(2000000000, start, start.getTime() - 4000000000L, new Date()));
    start = new Date();
    System.out.println("if(a && b) - a always false, b is condition on longs not date");
    System.out.println(timeAdv2(2000000000, start, start.getTime() - 4000000000L, new Date().getTime()));
    start = new Date();
    System.out.println("if(a) - a always false - condition on long");
    System.out.println(timeBasicL(2000000000, start.getTime(), start.getTime() - 4000000000L));
    start = new Date();
    System.out.println("if(a && b) - a always false, a and and b both conditions on long");
    System.out.println(timeAdvL(2000000000, start.getTime(), start.getTime() - 4000000000L, new Date().getTime()));
    start = new Date();
    System.out.println("if(a && b) - a always false, b always true");
    System.out.println(timeAdv(2000000000, start, start.getTime() - 4000000000L, new Date()));
    start = new Date();
    System.out.println("if(a && b) - both true");
    System.out.println(timeAdv(2000000000, start, start.getTime() + 1, new Date(start.getTime() + 4000000000L)));
    start = new Date();
    System.out.println("if(a && b) - a always true, b always false");
    System.out.println(timeAdv(2000000000, start, new Date(start.getTime() + 4000000001L).getTime(), new Date(start.getTime() + 4000000000L)));
}

private static int timeBasic(int size, Date start, long l) {
    long begin = System.currentTimeMillis();
    int c = 0;
    for (int i = 0; i < size; i++) {
        Date date = new Date(l++);
        if (start.before(date)) {
            c++;
        }
    }
    System.out.println(System.currentTimeMillis() - begin);
    return c;
}

private static int timeAdv(int size, Date start, long l, Date end) {
    long begin = System.currentTimeMillis();
    int c = 0;
    for (int i = 0; i < size; i++) {
        Date date = new Date(l++);
        if (start.before(date) && end.after(date)) {
            c++;
        }
    }
    System.out.println(System.currentTimeMillis() - begin);
    return c;
}

private static int timeAdv2(int size, Date start, long l, long end) {
    long begin = System.currentTimeMillis();
    int c = 0;
    for (int i = 0; i < size; i++) {
        Date date = new Date(l++);
        if (start.before(date) && end > l) {
            c++;
        }
    }
    System.out.println(System.currentTimeMillis() - begin);
    return c;
}

private static int timeBasicL(int size, long start, long l) {
    long begin = System.currentTimeMillis();
    int c = 0;
    for (int i = 0; i < size; i++) {
        l++;
        if (start < l) {
            c++;
        }
    }
    System.out.println(System.currentTimeMillis() - begin);
    return c;
}

private static int timeAdvL(int size, long start, long l, long end) {
    long begin = System.currentTimeMillis();
    int c = 0;
    for (int i = 0; i < size; i++) {
        l++;
        if (start < l && end > l) {
            c++;
        }
    }
    System.out.println(System.currentTimeMillis() - begin);
    return c;
}
}

在本地运行它时,我得到以下输出。有趣的测试是前三个测试。640是以毫秒为单位的doing的性能if(a)7079是doing 的性能if(a && b),whereabare 如上所述。710是做if(a && b)where bis的表现someDateAsLong < endDateAsLong

if(a) - a always false
640
0
if(a && b) - a always false, a and b both date method
7079
0
if(a && b) - a always false, b is condition on longs not date
710
0
if(a) - a always false - condition on long
639
0
if(a && b) - a always false, a and and b both conditions on long
708
0
if(a && b) - a always false, b always true
6873
0
if(a && b) - both true
11995
2000000000
if(a && b) - a always true, b always false
13746
0
4

2 回答 2

2

当您运行没有任何用处的代码时,您取决于 JIT 是否可以消除什么。在您的情况下,代码似乎正在被消除。

在快速情况下,每个循环需要 0.32 纳秒,这太短了,不现实。即大约一个时钟周期。较慢的情况是 3.6 ns,这更现实,但这可能意味着它只是大部分被优化掉了。

当您将行为从始终为假切换为始终为真(反之亦然)时,必须重新优化代码,您可能正在计时检测并替换代码所需的时间,而不是实际执行所需的时间。

我建议您添加一个外部循环以将 main() 中的所有代码运行 3 次,您应该会看到更显着的差异。

于 2013-10-14T11:12:25.210 回答
0

JVM 以一种在第一种情况下start.before(date)根本不评估的方式优化您的代码(我认为)。尝试运行它-Djava.compiler=NONE并查看结果。

于 2013-10-14T11:10:24.987 回答