【问题标题】:Is there optimization for constant function return value inside a loop? [duplicate]循环内的常量函数返回值是否有优化? [复制]
【发布时间】:2015-08-04 21:07:45
【问题描述】:

我的问题是关于gcc 编译器。

通常,在循环中,我必须使用一个函数返回的值,该值在整个循环期间是恒定的。

我想知道是否最好先将这个常量返回值存储在一个变量中(让我们想象一个长循环),或者像gcc 这样的编译器是否能够执行一些优化来缓存常量值,因为它会将其识别为不断抛出循环。

例如,当我遍历字符串中的字符时,我经常会这样写:

bool find_something(string s, char something)
{
    size_t sz = s.size();
    for (size_t i = 0; i != sz; i++)
        if (s[i] == something) return true;
    return false;
}

但是通过一个聪明的编译器,我可以使用以下(更短更清晰):

bool find_something(string s, char something)
{
    for (size_t i = 0; i != s.size(); i++)
        if (s[i] == something) return true;
    return false;
}

然后编译器可以检测到循环内的代码没有对string对象执行任何更改,然后将构建一个代码来缓存s.size()返回的值,而不是制作一个(较慢) 每次迭代的函数调用。

gcc有这样的优化吗?

【问题讨论】:

  • 这通常写成for (size_t i = 0, m = s.size(); i!=m; ++i)
  • @Dan,不是真的重复:最好的答案是:“内存引用和函数调用可能更困难。[关于优化]”。但我想更具体地了解gcc
  • 标准库容器的size 成员函数无论如何都会被内联,所以你担心的函数调用一开始就不存在。
  • @DavidSchwartz:我会使用 for rangefor (char c : s) {if (c == something) return true; } return false;)} ;-)(所以循环内外没有常数)。

标签: c++ gcc optimization


【解决方案1】:

通常,您的示例中没有任何内容使编译器无法在循环之前移动 .size() 计算。事实上,GCC 5.2.0 将为您展示的两种实现生成exactly the same code

然而,我强烈建议反对依赖这样的优化(在真正的性能关键代码中),因为某处的小改动(GCC 优化器,std::string 的实现细节,...)可以破坏 GCC 进行此优化的能力。

但是,我认为在通常 90% 的代码中编写更冗长的版本并没有什么意义,因为这些代码并不是真正的性能关键。

考虑到当前的 C++ 编译器,尽管我会选择更简洁的:

bool find_something(std::string s, char something)
{
    for (ch : s)
        if (ch == something) return true;
    return false;
}

顺便说一句,它也产生与 GCC 5.2.0 非常相似的机器代码。

【讨论】:

    【解决方案2】:

    编译器必须知道对象没有在不同的线程中修改。它可以判断如果对象不改变功能不会改变,但不能判断对象不会因其他刺激而改变。

    如果您包含某种形式的全程序优化,编译器将取消对具有大小的成员的调用

    【讨论】:

    • 编译器可以假设没有其他线程更改字符串,因为任何不受保护的并发写入都会调用 UB。
    • 所以我必须告诉编译器我没有使用任何令人不安的线程,然后,会有一个优化? (对不起,如果误解,我是法国人)
    • @yolenoyer 不,作为程序员,您必须确保在修改对象时不会访问任何对象。 (或使用为您执行此操作的工具。)编译器可能假定没有适当的同步措施(此处需要),其他线程不会修改对象。
    【解决方案3】:

    这取决于编译器是否可以将函数调用识别为常量。考虑以下可能驻留在编译器无法分析的外部库中的函数。

    int odd_size(string s) {
      static int a = 0;
      return a++;
    }
    

    无论输入参数如何,此函数都将返回不同的值。因此,即使传递的字符串对象保持不变,编译器也不能假定一个不变的返回值。不会应用任何优化。

    另一方面,如果编译器检测到常量函数调用(在您的示例中可能就是这种情况),它可能会将常量表达式移出循环。

    旧版本的gcc 有一个明确的选项-floop-optimize 负责该任务。来自gcc-3.4.5 documentation

    -floop-优化

    执行循环优化:将常量表达式移出循环,简化退出测试条件,并可选择进行强度降低和循环展开。

    在 -O、-O2、-O3、-Os 级别启用。

    我在当前版本的 gcc 中找不到此选项,但我很确定它们也包含此类优化。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-05
      • 2014-10-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多