我只是偶然发现了一个似乎有违反直觉的性能影响的变化。有人能为这种行为提供一个可能的解释吗?
原始代码:
for (int i = 0; i < ct; ++i) {
// do some stuff...
int iFreq = getFreq(i);
double dFreq = iFreq;
if (iFreq != 0) {
// do some stuff with iFreq...
// do some calculations with dFreq...
}
}在“性能传递”期间清理这些代码时,我决定将dFreq的定义移到if块中,因为它只在if中使用。有几个涉及dFreq的计算,所以我没有完全消除它,因为它确实节省了从int到double的多个运行时转换的成本。我期望没有任何表现上的差异,如果有的话,一个微不足道的改善。但其性能下降了近10%。我已经测量了很多次了,这确实是我所做的唯一的更改。上面显示的代码片段在其他几个循环中执行。在运行过程中,我会得到非常一致的时间安排,并且可以确定我所描述的更改会使性能下降10%。我预计性能会提高,因为int到double的转换只有在iFreq != 0时才会发生。
加密代码:
for (int i = 0; i < ct; ++i) {
// do some stuff...
int iFreq = getFreq(i);
if (iFreq != 0) {
// do some stuff with iFreq...
double dFreq = iFreq;
// do some stuff with dFreq...
}
}有人能解释一下吗?我使用的是VC++ 9.0和/O2。我只想弄明白我在这里不算什么。
发布于 2010-02-05 19:32:17
在使用dFreq进行计算之前,您应该将转换到iFreq的过程直接放在if()中。如果指令在代码中更高,则转换可以与整数计算并行执行。一个好的编译器可能会把它推到更高的位置,而一个不太好的编译器可能会把它留在原地。由于您将其移动到整数计算之后,它可能无法与整数代码并行运行,从而导致减速。如果它确实并行运行,那么可能没有什么改进,这取决于CPU (发出一个FP指令,其结果从未被使用过,在最初的版本中几乎没有效果)。
如果您真的想要提高性能,许多人已经完成了基准测试,并按此顺序排列了以下编译器:
( 1) ICC - Intel编译器2) GCC -一个好的第二名3) MSVC生成的代码与其他代码相比可能很差。
如果他们有-O3的话,你也可以试试它。
发布于 2010-02-05 19:10:23
也许getFreq的结果在第一种情况下保存在寄存器中,在第二种情况下写到内存中?也可能是,性能下降与CPU机制如流水线和/或分支预测有关。您可以检查生成的程序集代码。
发布于 2010-02-05 21:25:16
在我看来,这就像一个管道货摊
int iFreq = getFreq(i);
double dFreq = iFreq;
if (iFreq != 0) {允许双倍转换与其他代码并行进行,因为没有立即使用dFreq。它为编译器在存储iFreq和使用它之间提供了一些东西,因此这种转换很可能是“免费的”。
但
int iFreq = getFreq(i);
if (iFreq != 0) {
// do some stuff with iFreq...
double dFreq = iFreq;
// do some stuff with dFreq...
}在转换为double之后,可能会碰到存储/引用暂停,因为您马上就开始使用double值了。
现代处理器每个时钟周期都可以做多个事情,但前提是这些东西是独立的。引用相同寄存器的两个连续指令通常会导致失速。实际转换为double可能需要3个时钟,但除了第一个时钟之外,所有的时钟都可以与其他工作并行进行,前提是您不引用一两条指令的转换结果。
C++编译器正在非常擅长于重新排序指令来利用这一点,看起来您的更改失败了一些很好的优化。
另一种(不太可能)的可能性是,当到浮动的转换在分支之前时,编译器能够完全删除分支。在现代处理器中,无分支代码通常是主要的性能胜利。
看看编译器为这两种情况实际发出了哪些指令是很有趣的。
https://stackoverflow.com/questions/2209603
复制相似问题