同一功能的以下两个版本(基本上试图通过强力恢复密码)不会提供相同的性能:
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;
}
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 /架构的角度来理解,可以解释这种性能差异。 你的回答非常有帮助,再次感谢!
关键
答案 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将refi
和i
从ram的寄存器中取出。如果值不在缓存中,则有2个访问ram的权限。并且i
的值将写入ram。但是,根据线程和&amp ;;它总是需要不同的时间。流程安排。此外,如果值在虚拟内存中,则需要更长的时间。
i = length -1;
CPU还可以从ram或缓存中访问i
和length
。有相同数量的访问。此外,这里有一个减法,这意味着额外的CPU周期。这就是为什么你认为这个需要更长的时间来完成。这是预期的,但我上面提到的问题解释了为什么这需要更长的时间。
<强>求和强>
正如我解释的那样,这不是比较性能的方式。我认为,这些代码之间没有真正的区别。 CPU内部和编译器中都有很多优化。如果您反编译.class
文件,则可以看到优化的代码。
我的建议是最好尽量减少BigO运行时间分析。如果您找到更好的算法,那么它是优化代码的最佳方式。如果您的代码仍然存在瓶颈,您可以尝试微基准测试。
另见
答案 3 :(得分:0)
首先,您只需运行程序即可真正比较效果 - micro benchmarking in Java is complicated。
此外,现代CPU上的减法平均只需要时钟周期的三分之一。在3GHz CPU上,即0.1纳秒。没有任何事情告诉你减法实际上是因为编译器可能修改了代码。
所以: