代码之家  ›  专栏  ›  技术社区  ›  user12345

奇怪的C++性能差异?

  •  6
  • user12345  · 技术社区  · 16 年前

    我只是偶然发现了一个变化,似乎有违反直觉的性能后果。有人能为这种行为提供可能的解释吗?

    原始代码:

    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 如果 因此,我并没有完全消除它,因为它确实节省了多次运行时转换的成本 int double . 我不希望有任何性能上的差异,或者如果有任何差异的话,也不希望有微不足道的改进。然而,性能下降了近10%。我已经测量过很多次了,这确实是 只有 我做的改变。上面显示的代码片段在其他几个循环中执行。我在运行过程中得到了非常一致的计时,并且可以肯定我所描述的更改会使性能降低约10%。我希望性能会提高,因为 int 双重的 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。我只是想知道我在这里没有解释什么。

    8 回复  |  直到 16 年前
        1
  •  7
  •   phkahler    16 年前

    您应该将对dFreq的转换立即放在if()中 之前 使用iFreq进行计算。如果指令在代码中的位置更高,则转换可以与整数计算并行执行。一个好的编译器可能会把它推得更高,而一个不太好的编译器可能会把它留在原来的地方。由于在整数计算之后将其移动到,因此它可能无法与整数代码并行运行,从而导致速度减慢。如果它确实并行运行,那么根据CPU的不同,可能几乎没有任何改进(发出一条结果从未使用过的FP指令在原始版本中几乎没有效果)。

    如果您真的想提高性能,许多人已经完成了基准测试,并按照以下顺序对编译器进行了排名:

    1) 英特尔编译器 2) GCC-第二名

    如果他们有-O3,你也可以试试。

        2
  •  6
  •   MartinStettner    16 年前

    getFreq 在第一种情况下保存在寄存器中,在第二种情况下写入内存?性能下降也可能与CPU机制有关,如流水线和/或分支预测。 您可以检查生成的汇编代码。

        3
  •  4
  •   John Knoeller    16 年前

    int iFreq = getFreq(i);
        double dFreq = iFreq;
    
        if (iFreq != 0) {
    

    允许双精度转换发生 并行 使用其他代码 因为没有立即使用dFreq。它给了编译器一些东西 “免费”。

    int iFreq = getFreq(i);
    
    if (iFreq != 0) {
        // do some stuff with iFreq...
        double dFreq = iFreq;
        // do some stuff with dFreq...
    }
    

    C++编译器在重新排序指令方面很好地利用了这一点,看起来你的改变打败了一些不错的优化。

    另一种可能性(可能性较小)是,当到浮点的转换在分支之前时,编译器能够完全删除分支。在现代处理器中,无分支代码通常是一个主要的性能胜利。

        4
  •  3
  •   Gregor Brandt    16 年前

    尝试将dFreq的定义移到for循环的外部,但将赋值保持在for循环/if块的内部。

    可能是在if内部的堆栈every for循环上创建dFreq引起了问题(尽管编译器应该注意这一点)。如果dFreq变量位于四个循环中,则可能是编译器中的一个回归,它只创建了一次,而在if中,它每次都创建。

    double dFreq;
    int iFreq;
    for (int i = 0; i < ct; ++i) 
    {
        // do some stuff...
    
        iFreq = getFreq(i);
    
        if (iFreq != 0) 
        {
            // do some stuff with iFreq...
            dFreq = iFreq;
            // do some stuff with dFreq...
        }
    } 
    
        5
  •  2
  •   John Boker    16 年前

    也许编译器正在优化它,将定义置于for循环之外。如果编译器优化没有做到这一点,那么当您将其放入时。

        6
  •  1
  •   mxg    16 年前

    这一变化可能导致编译器禁用某些优化。如果将声明移到循环上方会发生什么?

        7
  •  1
  •   Klaim    16 年前

    这篇文章(有点老,但很有道理)说了一些类似的话: http://www.tantalon.com/pete/cppopt/asyougo.htm#PostponeVariableDeclaration

        8
  •  1
  •   Community Mohan Dere    9 年前

    很容易找到答案。只要20分钟 stackshots 慢版本和快版本。在慢速版本中,你将在大约2张照片上看到它正在做什么,而在快速版本中它没有做什么。您将看到它在汇编语言中停止的位置有细微的差别。