以下哪段代码在Java中更快?
a)以下哪段代码在Java中更快?,java,performance,micro-optimization,Java,Performance,Micro Optimization,a)for(inti=100000;i>0;i--){} b)for(inti=1;i0条件测试速度更快 底线是,对于任何非性能关键型应用程序,差异可能无关紧要。正如其他人指出的那样,有时使用++i而不是i++可能会更快,但是,特别是在for循环中,任何现代编译器都应该优化这种区别 也就是说,差异可能与为比较生成的底层指令有关。测试一个值是否等于0只是一个与非门。然而,测试一个值是否等于任意常数需要将该常数加载到寄存器中,然后比较两个寄存器。(这可能需要额外的一两次门延迟。)也就是说,对于流水线
for(inti=100000;i>0;i--){}
b)for(inti=1;i<100001;i++){}
答案就在(问题3)上。我就是不明白为什么?从网站:
三,。a
在许多编译器上,为反向循环发出的机器指令效率更高,因为测试零(因此对寄存器进行零运算)比加载常量值的立即数要快 另一方面,一个好的优化编译器应该能够检查内部循环,并确定向后运行不会导致任何副作用 顺便说一句,在我看来,这是一个糟糕的面试问题。除非您谈论的是一个运行了1000万次的循环,并且您已经确定,在许多重新创建前向循环值(n-i)的实例中,轻微的增益不会被超过,否则任何性能增益都将是最小的 一如既往,在没有性能基准测试的情况下,不要以较难理解的代码为代价进行微观优化。答案是a(正如您可能在网站上发现的那样)
我认为原因是终止循环的
I>0
条件测试速度更快 底线是,对于任何非性能关键型应用程序,差异可能无关紧要。正如其他人指出的那样,有时使用++i而不是i++可能会更快,但是,特别是在for循环中,任何现代编译器都应该优化这种区别
也就是说,差异可能与为比较生成的底层指令有关。测试一个值是否等于0只是一个与非门。然而,测试一个值是否等于任意常数需要将该常数加载到寄存器中,然后比较两个寄存器。(这可能需要额外的一两次门延迟。)也就是说,对于流水线和现代ALU,如果这种区别从一开始就很重要,我会感到惊讶。这些问题在很大程度上是一种无关的干扰,有些人会被它所困扰。称之为微优化崇拜或任何你喜欢的东西,但它是更快的循环或下降?认真地你可以选择适合你所做的事情。你不会为了节省两个时钟周期或者其他什么而编写代码 让编译器去做它想做的事情,让你的意图变得清晰(对编译器和读者都是如此)。另一种常见的Java悲观情绪是:
public final static String BLAH = new StringBuilder().append("This is ").append(3).append(' text").toString();
因为过度的连接确实会导致内存碎片,但对于常量,编译器可以(并且将)优化这一点:
public final static String BLAH = "This is a " + 3 + " test";
它不会优化第一个,第二个更容易阅读
那么(a>b)呢?a:b
vsMath.max(a,b)
?我知道我更喜欢读第二个,所以我不在乎第一个是否会产生函数调用开销
在这个列表中有一些有用的东西,比如知道finally
块没有在System.exit()
上调用。知道浮点除以0.0不会引发异常是有用的
但是不要费心去猜测编译器,除非它真的很重要(我敢打赌99.99%的时候它都不重要)。循环是相同的,除了一个关键部分: i>0; 和 i<100001 大于零检查是通过检查计算机的NZP(通常称为条件代码或负零或正位)位来完成的 NZP位在任何操作(如加载、和、加法等)时设置。都被执行了 大于检查不能直接利用该位(因此需要更长的时间…),一般的解决方案是将其中一个值设为负值(通过按位NOT,然后加1),然后将其添加到比较值中。如果结果为零,则它们相等。正,则第二个值(不是负)更大。负,则第一个值(负)更大。该检查所需时间比直接nzp检查稍长
虽然我不能100%确定这是它背后的原因,但这似乎是一个可能的原因…当您降至最低级别时(机器代码,但我将使用汇编,因为它主要是一对一映射),空循环递减到0和递增到50(例如)之间的区别通常是:
ld a,50 ld a,0
loop: dec a loop: inc a
jnz loop cmp a,50
jnz loop
这是因为大多数正常CPU中的零标志是在达到零时由减量指令设置的。当递增指令达到50时,通常不能这样说(因为该值与零不同,没有什么特别之处)。因此,您需要将寄存器与50进行比较,以设置零标志
但是,询问两个循环中的哪一个:
for(int i = 100000; i > 0; i--) {}
for(int i = 1; i < 100001; i++) {}
(dsw
当然是臭名昭著的do-something
assembler助记符)
由于每次迭代只需要对递增循环进行一次命中,并且每次迭代至少对减法进行一次命中(假设您使用的是i
,否则根本不需要循环),因此您应该使用更自然的版本
如果你需要数数,那就数数。如果需要倒计时,请倒计时。关于JVM中的零测试:显然可以使用来完成,而其他任何测试都需要在堆栈上添加额外的值
对
>0
的测试,如问题中所述,可能需要使用来完成,而对<100001
的测试则需要。在现代Java实现中,这是不正确的。
将总计10亿的数字作为基准:
Java(TM) SE Runtime Environment 1.6.0_05-b13
Java HotSpot(TM) Server VM 10.0-b19
up 1000000000: 1817ms 1.817ns/iteration (sum 499999999500000000)
up 1000000000: 1786ms 1.786ns/iteration (sum 499999999500000000)
up 1000000000: 1778ms 1.778ns/iteration (sum 499999999500000000)
up 1000000000: 1769ms 1.769ns/iteration (sum 499999999500000000)
up 1000000000: 1769ms 1.769ns/iteration (sum 499999999500000000)
up 1000000000: 1766ms 1.766ns/iteration (sum 499999999500000000)
up 1000000000: 1776ms 1.776ns/iteration (sum 499999999500000000)
up 1000000000: 1768ms 1.768ns/iteration (sum 499999999500000000)
up 1000000000: 1771ms 1.771ns/iteration (sum 499999999500000000)
up 1000000000: 1768ms 1.768ns/iteration (sum 499999999500000000)
down 1000000000: 1847ms 1.847ns/iteration (sum 499999999500000000)
down 1000000000: 1842ms 1.842ns/iteration (sum 499999999500000000)
down 1000000000: 1838ms 1.838ns/iteration (sum 499999999500000000)
down 1000000000: 1832ms 1.832ns/iteration (sum 499999999500000000)
down 1000000000: 1842ms 1.842ns/iteration (sum 499999999500000000)
down 1000000000: 1838ms 1.838ns/iteration (sum 499999999500000000)
down 1000000000: 1838ms 1.838ns/iteration (sum 499999999500000000)
down 1000000000: 1847ms 1.847ns/iteration (sum 499999999500000000)
down 1000000000: 1839ms 1.839ns/iteration (sum 499999999500000000)
down 1000000000: 1838ms 1.838ns/iteration (sum 499999999500000000)
使用int类型的sum大约快三倍,但随后sum溢出。
使用BigInteger时,速度要慢50倍以上:
BigInteger up 1000000000: 105943ms 105.943ns/iteration (sum 499999999500000000)
这是
long sum = 0;
for (int i = 0; i < limit; i++)
{
sum += i;
}
long sum = 0;
for (int i = limit - 1; i >= 0; i--)
{
sum += i;
}
BigInteger up 1000000000: 105943ms 105.943ns/iteration (sum 499999999500000000)
max benchmark ns
2 Forwards 4
2 Backwards 3
20 Forwards 9
20 Backwards 20
2000 Forwards 1007
2000 Backwards 1011
20000000 Forwards 9757363
20000000 Backwards 10303707
public static void main(String[] args) {
long time = 0;
for(int j=0; j<100; j++){
long startTime = System.nanoTime();
int i;
/*for(i=0;i<100;i++){
}*/
for(i=100;i>0;i--){
}
long endTime = System.nanoTime();
time += ((endTime-startTime));
}
time = time/100;
System.out.print("Time: "+time);
}