以下哪段代码在Java中更快?

以下哪段代码在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只是一个与非门。然而,测试一个值是否等于任意常数需要将该常数加载到寄存器中,然后比较两个寄存器。(这可能需要额外的一两次门延迟。)也就是说,对于流水线

a)
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
vs
Math.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);
}