Java:为什么计算速度比赋值(int)快?

时间:2013-08-15 06:38:30

标签: java performance variable-assignment

同一功能的以下两个版本(基本上试图通过强力恢复密码)不会提供相同的性能:

版本1:

private static final char[] CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".toCharArray();
private static final int N_CHARS = CHARS.length;
private static final int MAX_LENGTH = 8;

private static char[] recoverPassword()
{
   char word[];
   int refi, i, indexes[];

   for (int length = 1; length <= MAX_LENGTH; length++)
   {
      refi = length - 1;
      word = new char[length];
      indexes = new int[length];
      indexes[length - 1] = 1;

      while(true)
      {
         i = length - 1;
         while ((++indexes[i]) == N_CHARS)
         {
            word[i] = CHARS[indexes[i] = 0];
            if (--i < 0)
               break;
         }

         if (i < 0)
            break;

         word[i] = CHARS[indexes[i]];

         if (isValid(word))
            return word;
      }
   }
   return null;
}

版本2:

private static final char[] CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".toCharArray();
private static final int N_CHARS = CHARS.length;
private static final int MAX_LENGTH = 8;

private static char[] recoverPassword()
{
   char word[];
   int refi, i, indexes[];

   for (int length = 1; length <= MAX_LENGTH; length++)
   {
      refi = length - 1;
      word = new char[length];
      indexes = new int[length];
      indexes[length - 1] = 1;

      while(true)
      {
         i = refi;
         while ((++indexes[i]) == N_CHARS)
         {
            word[i] = CHARS[indexes[i] = 0];
            if (--i < 0)
               break;
         }

         if (i < 0)
            break;

         word[i] = CHARS[indexes[i]];

         if (isValid(word))
            return word;
      }
   }
   return null;
}

我希望版本2更快(这是唯一的区别):

i = refi;

...与版本1相比:

i = length -1;

然而,情况正好相反:版本1的速度超过3%! 有人知道为什么吗?这是由于编译器进行了一些优化吗?

谢谢大家的答案。 只是补充一点,目标实际上并不是优化这段代码(已经非常优化),而是从编译器/ CPU /架构的角度来理解,可以解释这种性能差异。 你的回答非常有帮助,再次感谢!

关键

4 个答案:

答案 0 :(得分:5)

很难在微基准测试中检查这一点,因为如果不读取生成的机器代码就无法确定代码是如何优化的,即使这样,CPU也可以做很多技巧来优化它。它将RISC样式指令中的x86代码转换为实际执行。

计算只需一个周期,CPU最多可以同时执行三个周期。对L1缓存的访问需要4个周期,对于L2,L3,主存储器需要11,40-75,200个周期。

在许多情况下,存储值以避免简单计算实际上较慢。使用除法和模数的BTW非常昂贵,在微调代码时,缓存这个值是值得的。

答案 1 :(得分:1)

正确的答案应该可以由反汇编程序检索(我的意思是.class - &gt; .java转换器), 但我的猜测是编译器可能已经决定完全摆脱iref并决定存储length - 1辅助寄存器。 我更像是一个c ++人,但我会先尝试:

const int refi = length - 1;
在for循环中

。你也应该使用

indexes[ refi ] = 1;

答案 2 :(得分:1)

比较代码的运行时间并未提供准确或隔离的结果

首先,这不是比较这样的表现的方式。这里需要运行时间分析。两个代码都具有相同的循环结构,并且它们的运行时间相同。运行代码时可能会有不同的运行时间。但是,它们通常与缓存命中,I / O时间,线程和放大器不同。流程安排。没有隔离区,代码总是在确切的时间内完成。

但是,您的代码仍然存在差异,以了解您应该查看CPU架构的差异。我基本上可以根据x86架构来解释它。

幕后会发生什么?

i = refi;

CPU将refii从ram的寄存器中取出。如果值不在缓存中,则有2个访问ram的权限。并且i的值将写入ram。但是,根据线程和&amp ;;它总是需要不同的时间。流程安排。此外,如果值在虚拟内存中,则需要更长的时间。

i = length -1;

CPU还可以从ram或缓存中访问ilength。有相同数量的访问。此外,这里有一个减法,这意味着额外的CPU周期。这就是为什么你认为这个需要更长的时间来完成。这是预期的,但我上面提到的问题解释了为什么这需要更长的时间。

<强>求和

正如我解释的那样,这不是比较性能的方式。我认为,这些代码之间没有真正的区别。 CPU内部和编译器中都有很多优化。如果您反编译.class文件,则可以看到优化的代码。

我的建议是最好尽量减少BigO运行时间分析。如果您找到更好的算法,那么它是优化代码的最佳方式。如果您的代码仍然存在瓶颈,您可以尝试微基准测试。

另见

Analysis of algorithms

Big O notation

Microprocessor

Compiler optimization

CPU Scheduling

答案 3 :(得分:0)

首先,您只需运行程序即可真正比较效果 - micro benchmarking in Java is complicated

此外,现代CPU上的减法平均只需要时钟周期的三分之一。在3GHz CPU上,即0.1纳秒。没有任何事情告诉你减法实际上是因为编译器可能修改了代码。

所以:

  • 您应该尝试检查generated assembly code
  • 如果您真的关心性能,请创建一个合适的微观基准。