C++ 在一个循环中是否有常数函数返回值的优化?

C++ 在一个循环中是否有常数函数返回值的优化?,c++,gcc,optimization,C++,Gcc,Optimization,我的问题是关于gcc编译器的 int odd_size(string s) { static int a = 0; return a++; } 通常,在一个循环中,我必须使用一个函数返回的值,该值在整个循环中是常量 我想知道是否最好先将这个常量返回值存储在一个变量中(让我们想象一个长循环),或者像gcc这样的编译器是否能够执行一些优化来缓存常量值,因为它会将它识别为循环中的常量 例如,当我循环字符串中的字符时,我经常这样写: bool find_something(string s,

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

int odd_size(string s) {
  static int a = 0;
  return a++;
}
通常,在一个循环中,我必须使用一个函数返回的值,该值在整个循环中是常量

我想知道是否最好先将这个常量返回值存储在一个变量中(让我们想象一个长循环),或者像
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;
}
但是有了一个聪明的编译器,我可以使用以下内容(更简短、更清晰):

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


使用
gcc
是否有这样的优化?

编译器必须知道对象没有在不同的线程中修改。它可以告诉我们,如果物体不改变,功能就不会改变,但是它不能告诉我们物体不会因为其他刺激而改变


如果包含某种形式的整个程序优化,编译器将解除对大小为的成员的调用。这取决于编译器是否能将函数调用标识为常量。考虑下面的函数,该函数可以驻留在无法由编译器分析的外部库中。
int odd_size(string s) {
  static int a = 0;
  return a++;
}
无论输入参数是什么,此函数都将返回不同的值。因此,即使传递的字符串对象保持不变,编译器也不能假定一个常量返回值。不会应用任何优化

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

较旧版本的
gcc
有一个明确的选项
-floop optimize
,负责该任务。从:

-地板优化

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

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


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

通常,在您的示例中没有任何东西使编译器无法在循环之前移动
.size()
计算。事实上,GCC5.2.0将为您展示的两种实现生成

然而,我强烈建议不要依赖这样的优化(在真正性能关键的代码中),因为某个地方(GCCs optimizer,
std::string
,…)的实现细节)的微小更改可能会破坏GCCs进行此优化的能力

然而,我不认为在通常的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_t i=0,m=s.size();i!=m;++i)的
。这就是所谓的。@Dan,不是真正的重复:最好的答案是:“内存引用和函数调用可能更难。[关于优化]”。但我想更具体地了解标准库容器的
gcc
成员函数的
size
无论如何都是内联的,因此您担心的函数调用一开始就不存在。@DavidSchwartz:我将使用range:
for(char c:s){if(c==something)return true;}return false;};-)(因此循环内外都没有常数)。编译器可以假设没有其他线程更改字符串,因为任何未受保护的并发写入都会调用UB。因此,我必须告诉编译器我没有使用任何干扰线程,然后,会有优化吗?(抱歉,如果误解了,我是法国人)@yolenoyer不,作为程序员,你必须确保在修改对象时,没有对象被访问。(或者使用为您做这件事的工具。)编译器可能会假设没有适当的同步措施(这里需要)其他线程都不会修改对象。