【问题标题】:Counting down or Counting up, Which one is faster? [duplicate]倒数或倒数,哪个更快? [复制]
【发布时间】:2012-11-26 00:22:17
【问题描述】:

可能重复:
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

【问题讨论】:

  • 运行它并自己计时。我记得看到一些关于递减比递增慢的事情,尽管时间很短。 10 毫秒左右。
  • 在性能方面它们都是一样的
  • 除非++-- 的表现不同,否则没有实际区别。即使有,您也需要一个扩展循环才能真正注意到它,而这种方式可能会损害您的应用程序的整体性能。
  • @Max 你是什么意思,“10 毫秒左右”?我们在这里说的是纳秒。
  • 这不是 inc 或 dec 的问题,它更多的是与 0 或 n 进行比较

标签: java performance loops


【解决方案1】:

永远不要怀疑;用谷歌卡尺找出来。由于围绕零与上限以及递增与递减测试的相对权重进行了相当多的讨论,以下是所有这些情况的笛卡尔积:

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 的效果要小。

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

为了指出这些差异有多么微妙,这里的代码几乎相同,但现在它使用longs(我包括第一个示例中的一个方法,以保持规模):

  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 ==============================

主要结论:永远不要在如此低的水平上对性能做出任何假设。编写完整的代码并对其进行整体测试,因为总会有一些你没有考虑到的东西,这完全颠覆了局面。

【讨论】:

  • 伙计,这太棒了。但是为什么我不能下载呢?这是正确的下载链接 - code.google.com/p/caliper 吗?
  • 没有可下载的工件,只需在code.google.com/p/caliper/source/checkout查看源代码即可。
  • 我不相信这是一个重要的结果。我怀疑,在相反方向引入求和可能会对所用时间产生更大的影响,并且由于您正在迭代到 INTEGER.MAX_VALUE,并求和为一个 int,您也会遇到整数溢出。
  • 问题不是递减与递增,而是与零的比较。你。哦
  • 这毫无意义。尝试将 n 设为 volatile 字段(或在循环中执行一些操作以防止编译器识别出 n 在循环期间是恒定的),您的结果将完全不同。
【解决方案2】:

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

这纯粹是学术性的,恕我直言。它们在本质上是“相同的”,所以你应该实现一个对你之后维护你的代码的人来说更合乎逻辑和更容易理解的那个。

【讨论】:

    【解决方案3】:

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

    如果在您测试之后,分析显示某个特定循环存在问题,那么请担心优化该循环,关注循环主体而不是诸如递增和递减之类的事情。

    【讨论】:

      【解决方案4】:

      答案取决于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;
          }
      }
      

      【讨论】:

        【解决方案5】:

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

        【讨论】:

          【解决方案6】:

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

          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

          【讨论】:

            猜你喜欢
            • 2011-02-18
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-06-02
            • 2023-03-04
            • 2016-12-02
            • 1970-01-01
            相关资源
            最近更新 更多