使用日期和&&时的性能问题

时间:2013-10-14 10:46:39

标签: java performance date

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

基本上我发现我可以构造两个条件a和b,使得if(a)if(a && b)快10倍,即使a总是假的,这似乎直接与事实上Java应该对条件进行惰性评估。

asomeDate.after(startdate)bsomeDate.before(enddate)时,就会发生这种情况 - 即一个相当标准的范围条件。

下面我附上了用于显示此特定问题的代码。我在Windows 7上使用Java 7运行代码,我的一位同事在Windows 7上使用java 6,7和8运行它 - 都具有相同的结果。

有谁可以解释为什么会发生这种情况?

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是执行if(a)的性能(以毫秒为单位),7079是执行if(a && b)的效果,其中ab如上所述。 710if(a && b) b 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

2 个答案:

答案 0 :(得分:2)

当您运行没有做任何有用的代码时,您依赖于JIT可以消除或不消除的内容。在您的情况下,似乎代码正在消除。

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

当您将行为从始终为false切换为始终为真且反之亦然时,必须重新优化代码,您可以计算检测此代码并替换代码所需的时间,而不是实际执行所需的时间

我建议你添加一个外部循环来运行main()中的所有代码三次,你应该看到一个甚至是戏剧性的差异。

答案 1 :(得分:0)

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