我正在对方法的性能进行调查,最后发现开销是由if else语句的“else”部分引起的。我编写了一个小程序来说明性能差异,即使代码的else部分永远不会被执行:
public class TestIfPerf
{
public static void main( String[] args )
{
boolean condition = true;
long time = 0L;
int value = 0;
// warm up test
for( int count=0; count<10000000; count++ )
{
if ( condition )
{
value = 1 + 2;
}
else
{
value = 1 + 3;
}
}
// benchmark if condition only
time = System.nanoTime();
for( int count=0; count<10000000; count++ )
{
if ( condition )
{
value = 1 + 2;
}
}
time = System.nanoTime() - time;
System.out.println( "1) performance " + time );
time = System.nanoTime();
// benchmark if else condition
for( int count=0; count<10000000; count++ )
{
if ( condition )
{
value = 1 + 2;
}
else
{
value = 1 + 3;
}
}
time = System.nanoTime() - time;
System.out.println( "2) performance " + time );
}
}
并使用java -classpath . -Dmx=800m -Dms=800m TestIfPerf
运行测试程序。
我在Mac和Linux Java上使用1.6最新版本执行此操作。一直是没有else的第一个基准测试比使用else部分的第二个基准测试快得多,即使代码的结构使得else部分由于条件而永远不会被执行。据我所知,差异可能不大,但相对性能差异很大。我想知道是否有人对此有任何见解(或者有些事我做错了)。
Linux基准测试(纳米级)
Mac基准(纳米)
答案 0 :(得分:6)
你的考试是铺位。如果我交换测试条件,我会得到完全相反的结果:
1) performance 5891113
2) performance 15216601
2) performance 5428062
1) performance 15087676
它可能与JVM在执行过程中优化代码有关。如果我复制/粘贴条件几次,我得到这个:
2) performance 6258694
1) performance 34484277
2) performance 978
1) performance 978
2) performance 908
答案 1 :(得分:2)
有两种可能的解释:
你得到的时间被基准缺陷扭曲了。你做了很多错事 - 见How do I write a correct micro-benchmark in Java?
真正具有else
的版本每次循环迭代需要稍长的时间。如果是这种情况,则有许多可能的解释。掌握它的最佳方法是查看JIT编译器生成的本机代码并分析其性能。
但最重要的是,这并不令人惊讶(见上文),或绝大多数Java应用程序的任何实际后果。应用程序确定是否需要“if then”或“if then else”。
令人怀疑的是,您可能从这样的人工微基准测试中学到的任何内容对于实际代码都具有指导意义。 JIT编译器可能会以比测试运行更复杂的级别优化代码。你可能会在这里看到的(如果你的基准没有缺陷)不太可能反映在真实的应用程序中。
答案 2 :(得分:0)
如果你在第二个测量中注释掉else分支,它会变得更奇怪:它仍然更慢,尽管它现在是相同的代码。好消息是,如果您使用单独的方法提取此代码,则第二次运行速度会快得多。
我唯一能想到的是JVM只优化了长方法的第一部分。是的:如果我首先使用if-else测量测量,那就更快了。
答案 3 :(得分:0)
必须与VM init(预热很短)或时间测量中的抖动(与VM启动相关)有关。
如果你交换循环,循环2变得更快: - )
一般情况下,热点JIT是不错的,但不可靠,不具有确定性。 在java中获得最佳性能
一般来说,使用微基准来证明性能模式非常困难,因为您不知道究竟是什么触发了内联,jit编译和进一步的运行时优化。 JIT中存在阈值,因此可能会导致性能降低,这只是因为您向方法添加了语句或添加了现有类的子类。
答案 4 :(得分:0)
Java代码:
for (int count = 0; count < 10000000; count++) {
// start of the if
if (cond) {
value = 1 + 2;
}
// end of the if
}
Java字节码:
7: lstore_3 //store a long value in a local variable 3
8: iconst_0 //load the int value 0 onto the stack
9: istore 5 //store int value into variable #index
11: goto 23 //goes to another instruction at branchoffset
14: iload_1 //load an int value from local variable 1
15: ifeq 20 //if value is 0, branch to instruction at branchoffset
18: iconst_3 //load the int value 3 onto the stack
19: istore_2 //store int value into variable 2
20: iinc 5, 1 //increment local variable #index by signed byte const
23: iload 5 //load an int value from a local variable #index
25: ldc #22; //push a constant #index from a constant pool (String, int or float) onto the stack - int 10000000
27: if_icmplt 14 //if value1 is less than value2, branch to instruction at branchoffset
Java代码:
for (int count = 0; count < 10000000; count++) {
// start of the if
if (cond) {
value = 1 + 2;
} else {
value = 1 + 3;
}
// end of the if
}
Java字节码:
63: lstore_3 //store a long value in a local variable 3
64: iconst_0 //load the int value 0 onto the stack
65: istore 5 //store int value into variable #index
67: goto 84 //goes to another instruction at branchoffset
70: iload_1 //load an int value from local variable 1
71: ifeq 79 //if value is 0, branch to instruction at branchoffset
74: iconst_3 //load the int value 3 onto the stack
75: istore_2 //store int value into variable 2
76: goto 81 //goes to another instruction at branchoffset
79: iconst_4 //load the int value 4 onto the stack
80: istore_2 //store int value into variable 2
81: iinc 5, 1 //increment local variable #index by signed byte const
84: iload 5 //load an int value from a local variable #index
86: ldc #22; //push a constant #index from a constant pool (String, int or float) onto the stack - int 10000000
88: if_icmplt 70 //if value1 is less than value2, branch to instruction at branchoffset
答案 5 :(得分:-1)
public class Main {
public static void main(String[] args) {
boolean cond = true;
int nothing = 0;
for (int i = 0; i < 20; i++) {
int value = 0;
long time = System.nanoTime();
for (int count = 0; count < 10000000; count++) {
if (cond) {
value = 1 + 2;
}
}
time = System.nanoTime() - time;
System.out.println("1) performance: " + time);
nothing = value; // prevent java ignoring value
value = 0;
time = System.nanoTime();
for (int count = 0; count < 10000000; count++) {
if (cond) {
value = 1 + 2;
} else {
value = 1 + 3;
}
}
time = System.nanoTime() - time;
System.out.println("2) performance: " + time);
nothing = value; // prevent java ignoring value
}
nothing = nothing + 1;
}
}
结果如下:
1) performance: 1797000
2) performance: 3742000
1) performance: 7290000
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 1000
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 1000
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 0
2) performance: 0
1) performance: 0
2) performance: 0