在评估一些非常具体的if条件时,我遇到了一些非常奇怪的性能行为。
基本上我发现我可以构造两个条件a和b,使得if(a)
比if(a && b)
快10倍,即使a
总是假的,这似乎直接与事实上Java应该对条件进行惰性评估。
当a
为someDate.after(startdate)
而b
为someDate.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)
的效果,其中a
和b
如上所述。 710
是if(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
答案 0 :(得分:2)
当您运行没有做任何有用的代码时,您依赖于JIT可以消除或不消除的内容。在您的情况下,似乎代码正在消除。
在快速的情况下,每个循环都需要0.32纳秒,这太短了不太现实。即大约一个时钟周期。 较慢的情况是3.6 ns,这更现实,但它可能意味着它只是大部分优化了。
当您将行为从始终为false切换为始终为真且反之亦然时,必须重新优化代码,您可以计算检测此代码并替换代码所需的时间,而不是实际执行所需的时间
我建议你添加一个外部循环来运行main()中的所有代码三次,你应该看到一个甚至是戏剧性的差异。
答案 1 :(得分:0)
JVM以第一种情况start.before(date)
未评估的方式优化您的代码(我认为)。尝试使用-Djava.compiler=NONE
运行它并查看结果。