user2878443
user2878443

Reputation: 53

Performance issue when using date and &&

I am experiencing some quite strange performance behaviour when evaluating some very specific if conditions.

Basically I have found that I can construct two conditions a and b such that if(a) is 10 times faster than if(a && b) even if a is always false, which seems to clash directly with the fact that Java should do lazy evaluation of conditions.

The case occurs when I have a to be someDate.after(startdate) and b to be someDate.before(enddate) - i.e a fairly standard range condition.

Below I've attached the code I use to show this particular issue. I've run the code on Windows 7 with Java 7, and one of my colleagues have run it on Windows 7 with java 6,7 and 8 - all with the same result.

Can anyone explain why this could happen?

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;
}
}

When running it locally I get the following output. The interesting tests are the three first ones. 640 is the performance in milliseconds of doing if(a), 7079 is the performance of doing if(a && b), where a and b are as described above. 710 is the performance of doing if(a && b) where b is 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

Upvotes: 1

Views: 128

Answers (2)

Peter Lawrey
Peter Lawrey

Reputation: 533500

When you run code which doesn't do anything useful, you are dependant on what the JIT can eliminate or not. In your case it appears the code is being eliminated.

In the fast case, each loop is taking 0.32 nano-seconds, which is far too short to be realistic. i.e. one clock cycle approx. The slower case is 3.6 ns which is more realistic but it could mean it is only mostly optimised away.

When you switch behaviours from always false to always true and visa versa, the code has to be re-optimised and you may be timing how lon it takes to detect this and replace the code, not how long it takes to actual execute.

I suggest you add an outer loop to run all the code in main() three times and you should see an even mroe dramatic difference.

Upvotes: 2

Dia
Dia

Reputation: 271

The JVM optimizes your code in a way that in the first case start.before(date) isn't evaluated at all (I think). Try running it with -Djava.compiler=NONE and see the results.

Upvotes: 0

Related Questions