倒数或倒数,哪一个更快?

时间:2012-12-07 19:16:45

标签: java performance loops

  

可能重复:
  Which of these pieces of code is faster in Java?

如果我写一个循环

for (int i=n; i>=0; i--)

和其他人一样

for (int i=0; i<=n; i++)

在java中哪一个更快,为什么?..说 n = 10000

6 个答案:

答案 0 :(得分:9)

不要怀疑;使用Google Caliper查找。由于围绕零与上限和递增与递减的测试的相对权重进行了相当多的讨论,这里是所有这些情况的笛卡尔积:

import java.util.Random;

import com.google.caliper.Runner;
import com.google.caliper.SimpleBenchmark;

public class Performance extends SimpleBenchmark {
  static final Random rnd = new Random();

  public int timeDecrementToZero(int reps) {
    int sum = rnd.nextInt();
    for (int i = 0; i < reps; i++) {
      for (int j = Integer.MAX_VALUE; j >= 0; j--) sum += j;
    }
    return sum;
  }
  public int timeDecrementFromZero(int reps) {
    int sum = rnd.nextInt();
    for (int i = 0; i < reps; i++) {
      for (int j = 0; j > Integer.MIN_VALUE; j--) sum += j;
    }
    return sum;
  }
  public int timeIncrementFromZero(int reps) {
    int sum = rnd.nextInt();
    for (int i = 0; i < reps; i++) {
      for (int j = 0; j < Integer.MAX_VALUE; j++) sum += j;
    }
    return sum;
  }
  public int timeIncrementToZero(int reps) {
    int sum = rnd.nextInt();
    for (int i = 0; i < reps; i++) {
      for (int j = Integer.MIN_VALUE; j < 0; j++) sum += j;
    }
    return sum;
  }

  public static void main(String... args) {
    Runner.main(Performance.class, args);
  }
}

结果:

 0% Scenario{vm=java, trial=0, benchmark=DecrementToZero} 984060500.00 ns; σ=30872487.22 ns @ 10 trials
25% Scenario{vm=java, trial=0, benchmark=DecrementFromZero} 982646000.00 ns; σ=35524893.00 ns @ 10 trials
50% Scenario{vm=java, trial=0, benchmark=IncrementFromZero} 1023745500.00 ns; σ=24828496.82 ns @ 10 trials
75% Scenario{vm=java, trial=0, benchmark=IncrementToZero} 1081112500.00 ns; σ=20160821.13 ns @ 10 trials

        benchmark   ms linear runtime
  DecrementToZero  984 ===========================
DecrementFromZero  983 ===========================
IncrementFromZero 1024 ============================
  IncrementToZero 1081 ==============================

显然,限制是否为零比使用inc与dec的影响小。

让我们改变一下......

要指出这些差异有多么琐碎,这里几乎是相同的代码,但现在它使用long s(我在第一个示例中包含一个方法,以维持比例):

  public int timeDecrementFromZeroInt(int reps) {
    int sum = rnd.nextInt();
    for (int i = 0; i < reps; i++) {
      for (int j = 0; j > Integer.MIN_VALUE; j--) sum += j;
    }
    return sum;
  }
  public long timeDecrementFromZero(int reps) {
    long sum = rnd.nextLong();
    for (long i = 0; i < reps; i++) {
      for (long j = 0; j > Integer.MIN_VALUE; j--) sum += j;
    }
    return sum;
  }
  public long timeIncrementFromZero(int reps) {
    long sum = rnd.nextLong();
    for (long i = 0; i < reps; i++) {
      for (long j = 0; j < Integer.MAX_VALUE; j++) sum += j;
    }
    return sum;
  }
  public long timeDecrementToZero(int reps) {
    long sum = rnd.nextLong();
    for (long i = 0; i < reps; i++) {
      for (long j = Integer.MAX_VALUE; j >= 0; j--) sum += j;
    }
    return sum;
  }
  public long timeIncrementToZero(int reps) {
    long sum = rnd.nextLong();
    for (long i = 0; i < reps; i++) {
      for (long j = Integer.MIN_VALUE; j < 0; j++) sum += j;
    }
    return sum;
  }

结果:

 0% Scenario{vm=java, trial=0, benchmark=DecrementFromZeroInt} 978513000.00 ns; σ=14861284.82 ns @ 10 trials
20% Scenario{vm=java, trial=0, benchmark=DecrementFromZero} 2160652000.00 ns; σ=13825686.87 ns @ 3 trials
40% Scenario{vm=java, trial=0, benchmark=IncrementFromZero} 2153370000.00 ns; σ=6318160.49 ns @ 3 trials
60% Scenario{vm=java, trial=0, benchmark=DecrementToZero} 4379893000.00 ns; σ=8739917.79 ns @ 3 trials
80% Scenario{vm=java, trial=0, benchmark=IncrementToZero} 4383569000.00 ns; σ=5798095.89 ns @ 3 trials

           benchmark   ms linear runtime
DecrementFromZeroInt  979 ======
   DecrementFromZero 2161 ==============
   IncrementFromZero 2153 ==============
     DecrementToZero 4380 =============================
     IncrementToZero 4384 ==============================

主要结论:永远不要假设性能如此之低。编写完整的代码并将其作为一个整体进行测试,因为总会有其他一些你没有考虑到的东西,这会彻底改变表格。

答案 1 :(得分:4)

CPU可能有一种更快的方法,即将数字(i)与0进行比较,而不是与另一个任意数字(n)进行比较。这理论上会使减量版本更快。

这纯粹是学术性的,恕我直言。它们基本上都是“相同的”,所以你应该实现一个更合乎逻辑且易于理解的代码,以保护你的代码。

答案 2 :(得分:2)

只需按照编写它们最有意义的方式编写循环。您不太可能(a)做任何时间紧迫的事情,在程序的整个持续时间内几个额外的纳秒将产生影响,并且(b)您的代码如此优化以至于瓶颈是循环中的递增或递减操作。

如果在测试之后,分析显示某个特定循环是一个问题,那么就要担心优化该循环,关注循环体而不是增量和减量之类的东西。

答案 3 :(得分:2)

答案是它取决于n是什么。倒计时时,代码只需访问n一次。在计数时,这可能不是真的。因此,例如,如果nvolatile字段,或者循环体中的某些内容可能会更改n的值,则每次循环时都需要查找该值。这将显着减慢循环。

使用此代码,向上计数比向下计数慢几百倍:

public class Counts {
    private static final int ITERS = 100000;
    volatile int n = 1000;

    public long countUp() {
        long start = System.nanoTime();
        for (int iter = 0; iter < ITERS; ++iter) {
            for (int i = 0; i < n; ++i) {
                // do nothing
            }
        }
        return System.nanoTime() - start;
    }

    public long countDown() {
        long start = System.nanoTime();
        for (int iter = 0; iter < ITERS; ++iter) {
            for (int i = n - 1; i >= 0; --i) {
                // do nothing
            }
        }
        return System.nanoTime() - start;
    }
}

答案 4 :(得分:0)

如果存在任何可测量的差异,则通过与0比较的Variant更快,因为在CPU级别上,与0比较更快。 但是在大多数情况下,最好使用良好的可读代码

答案 5 :(得分:0)

在大多数情况下,与其他人理解正在发生的事情所需的时间相比,时间差异微不足道。只需使用最容易遵循的内容。如果你想测试它,你可以运行这样的东西:

long startTime = System.nanoTime();  
long duration, endTime;  

for (int i=0;i<1000 ;i++ ) {  
        //function
   }  

endTime = System.nanoTime();  
duration = endTime - startTime;  

System.out.printf("The duration was %d.%03d microseconds%n", duration / 1000, duration % 1000); 

增量和减量。

更大的问题是事后增加/减少。从第2页底部开始有一个非常好的解释:http://www.iar.com/Global/Resources/Developers_Toolbox/C_Cplusplus_Programming/Writing%20optimizer-friendly%20code.pdf