在循环中有常量函数返回值的优化吗?

Is there optimization for constant function return value inside a loop?

本文关键字:优化 返回值 函数 循环 常量      更新时间:2023-10-16

我的问题是关于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有这样的优化吗?

一般来说,在您的示例中没有什么可以使编译器不可能在循环之前移动.size()计算。事实上,GCC 5.2.0将为您展示的两个实现生成完全相同的代码。

然而,我强烈建议反对依赖这样的优化(在真正的性能关键代码中),因为某个地方的一个小变化(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的机器码非常相似。

编译器必须知道该对象没有在其他线程中被修改。它能判断出如果物体不变,函数也不会改变,但它不能判断物体在其他刺激下不会改变。

如果包含某种形式的整个程序优化,编译器将展开对具有size的成员的调用

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

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

无论输入参数如何,该函数都将返回不同的值。因此,即使传递的字符串对象保持不变,编译器也不能假定返回值为常量。

另一方面,如果编译器检测到常量函数调用,它可能会将常量表达式移出循环。

旧版本的gcc有一个明确的选项-floop-optimize来负责这个任务。来自gcc-3.4.5文档:

-floop-optimize

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

在-O, -O2, -O3, -O级别启用。

我在gcc的当前版本中找不到这个选项,但我很确定它们也包括这种类型的优化