【问题标题】:c++ for loop optimization questionc++ for循环优化问题
【发布时间】:2011-07-04 05:22:47
【问题描述】:

我在 VC++ 中有如下外观代码:

for (int i = (a - 1) * b; i < a * b && i < someObject->someFunction(); i++)
{
    // ...
}

据我所知,编译器优化了所有这些算术运算,它们不会在每个循环中执行,但我不确定他们是否能判断出上面的函数每次也返回相同的值,但它没有每次都需要调用。

将所有计算保存到变量中是更好的做法,还是仅仅依靠编译器优化来获得更具可读性的代码?

int start = (a - 1) * b;
int expra = a * b;
int exprb = someObject->someFunction();
for (int i = startl i < expra && i < exprb; i++)
{
    // ...
}

【问题讨论】:

  • 这里,++i 优于 i++
  • @Nawaz 只要 i 只是一个整数,它就不应该有所作为。
  • @Timbo:取决于编译器是否对此进行优化。毕竟在i++的情况下,会创建一个临时的!
  • @Nawaz:你敢找一个不执行优化的编译器。
  • 感谢您的所有回答!我将选择一个保存函数结果并将算术保留在表达式中的方法,因为保存它们只会创建不必要的变量名。

标签: c++ visual-c++ optimization


【解决方案1】:

简短回答:视情况而定。如果编译器可以推断出每次运行someObject-&gt;someFunction() 并缓存一次结果都产生相同的效果,则允许(但不保证)这样做。这种静态分析是否可行取决于你的程序:具体来说,someObject 的静态类型是什么,它的动态类型应该是什么,以及someFunction() 实际做了什么,是不是virtual,等等开。

一般来说,如果只需要执行一次,请以这样的方式编写代码,使其可以只执行一次,无需担心编译器可能会发生什么 正在做:

int start = (a - 1) * b;
int expra = a * b;
int exprb = someObject->someFunction();
for (int i = start; i < expra && i < exprb; i++)
    // ...

或者,如果你想简洁:

for (int i = (a - 1) * b, expra = a * b, exprb = someObject->someFunction();
     i < expra && i < exprb; i++)
    // ...

【讨论】:

  • @CashCow:我没有看到反对票,所以它肯定在过去几分钟内被更改了。
  • 关于编译器,我认为在 gcc 上使用属性 pure 将有助于编译器优化调用。显然是不可携带的。
【解决方案2】:

根据我的经验,VC++ 编译器不会优化函数调用,除非它可以在编译调用代码时看到函数实现。所以将调用移到循环之外是个好主意。

【讨论】:

    【解决方案3】:

    如果一个函数与其调用者位于同一个编译单元中,编译器通常可以推断出关于它的一些事实——例如它的输出可能不会因后续调用而改变。但是,一般情况下并非如此。

    在您的示例中,为这些简单的算术表达式分配变量并不会真正改变生成的目标代码的任何内容,并且在我看来,这会降低代码的可读性。除非你有一堆不能合理地放在一两行内的长表达式,否则你应该避免使用临时变量——如果没有其他原因,那么只是为了减少命名空间污染。

    使用临时变量意味着程序员的大量管理开销,以便将它们分开并避免意外的副作用。这也使得重用代码 sn-ps 变得更加困难。

    另一方面,将函数的结果分配给变量可以通过显式避免多次函数调用来帮助编译器更好地优化代码。

    就个人而言,我会这样做:

    int expr = someObject->someFunction();
    for (int i = (a - 1) * b; i < a * b && i < expr; i++)
    {
        // ...
    }
    

    【讨论】:

      【解决方案4】:

      编译器无法对您的函数是否每次都返回相同的值做出任何假设。假设您的对象是一个套接字,编译器怎么可能知道它的输出是什么?

      此外,编译器可以在此类循环中进行的优化很大程度上取决于 a 和 b 是否声明为 const,以及它们是否是本地的。使用高级优化方案,它可能能够推断出 a 和 b 既没有在循环中也没有在你的函数中被修改(同样,你可能会想象你的对象持有对它们的一些引用)。

      好吧,简而言之:选择您的代码的第二个版本!

      【讨论】:

        【解决方案5】:

        很有可能编译器每次都会调用该函数。

        如果你关心代码的可读性,那么如何使用:

        int maxindex = min (expra, exprb);
        for (i=start; i<maxindex; i++)
        

        恕我直言,长行不会提高可读性。

        编写短行并执行多个步骤以获得结果,不会影响性能,这正是我们使用编译器的原因。

        【讨论】:

        • 是的,对分解好的代码进行优化通常更简单。
        【解决方案6】:

        实际上你可能要问的是编译器是否会内联函数 someFunction() 以及它是否会在每个循环中看到 someObject 是同一个实例,如果两者都这样做,它可能会“缓存”返回值和不要一直重新评估它。

        这在很大程度上可能取决于您使用的优化设置,以及 VC++ 以及任何其他编译器,尽管我不确定 VC++ 是否为您提供了与 gnu 一样多的标志。

        我经常发现程序员依靠编译器来优化他们可以轻松优化自己的东西,这令人难以置信。如果您知道每次的计算结果都相同,只需将表达式移动到 for 循环的第一部分:

        只需这样做,不要依赖编译器:

        for (int i = (a - 1) * b, iMax = someObject->someFunction();
               i < a * b && i < iMax; ++i) 
        {
          // body
        }
        

        【讨论】:

          猜你喜欢
          • 2018-12-23
          • 1970-01-01
          • 2010-11-15
          • 2011-08-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-09-28
          • 2015-04-15
          相关资源
          最近更新 更多