【问题标题】:Evaluation of constants in for loop condition评估 for 循环条件中的常量
【发布时间】:2014-10-15 12:05:20
【问题描述】:
for(int i = 0; i < my_function(MY_CONSTANT); ++i){
    //code using i
}

在这个例子中,my_function(MY_CONSTANT) 会在每次迭代时被评估,还是会自动存储?这是否取决于使用的优化标志?

【问题讨论】:

  • 将在每次迭代中进行评估。最好将其存储为变量。
  • loop condition evaluation 的可能重复项
  • @guygree,不是重复的,那里没有函数调用。
  • @Basilevs,我认为这不会改变这里的情况,表达式(无论那个表达式是什么)每次都需要重新评估。我认为特别是this 回答总结得很好
  • 不过,原始问题完全不同。

标签: c++ loops optimization as-if


【解决方案1】:

它必须每次调用该函数一样工作。

但是,如果编译器能够证明每次的函数结果都是一样的,那么它可以在“as if”规则下进行优化。

例如这通常发生在对标准容器调用 .end() 时。


一般建议:当不确定是否对一段代码进行微优化时,

  1. 不要这样做。
  2. 如果您仍在考虑这样做,请衡量
  3. 嗯,还有第三点,但我忘记了,也许是,还是等等吧。

换句话说,是否使用变量取决于代码的清晰程度,而不是想象的性能。

【讨论】:

  • 感谢您的额外建议。当我写我的问题时,我想到MY_CONSTANT 是由“#define”指令预定义的,并阅读上面与我自己相关的问题,似乎最近的编译器会存储它。非常感谢!
【解决方案2】:

每次迭代都会对其进行评估。您可以通过执行类似的操作来节省额外的计算时间

const int stop = my_function(MY_CONSTANT);
for(int i = 0; i < stop; ++i){
    //code using i
}

【讨论】:

  • 甚至没有必要污染周围的范围:for(int i = 0, stop = my_function(MY_CONSTANT); i &lt; stop; ++i)。我的大部分for 循环最近看起来都是这样的。
【解决方案3】:

as-if rule 下的现代优化编译器可能能够在您在comment here 中概述的情况下优化函数调用。 as-if rule 表示符合标准的编译器仅具有模拟可观察行为,我们可以通过转到草案 C++ 标准部分 1.9 Program execution 看到这一点:

[...]相反,需要符合要求的实现来模拟(仅) 抽象机器的可观察行为,如下所述。5

因此,如果您使用 常量表达式 并且 my_function 没有可观察到的副作用,则可以对其进行优化。我们可以做一个简单的测试(see it live on godbolt):

#include <stdio.h>

#define blah 10

int func( int x )
{
  return x + 20 ;
}

void withConstant( int y )
{
  for(int i = 0; i < func(blah); i++)
  {
    printf("%d ", i ) ;
  }
}

void withoutConstant(int y)
{
  for(int i = 0; i < func(i+y); i++)
  {
    printf("%d ", i ) ;
  }
}

对于withConstant,我们可以看到它优化了计算:

cmpl    $30, %ebx   #, i

即使在 withoutConstant 的情况下,它也会内联计算而不是执行函数调用:

leal    0(%rbp,%rbx), %eax  #, D.2605

【讨论】:

    【解决方案4】:

    如果my_function 被声明为 constexpr 并且参数实际上是一个常量,则在编译时计算该值,从而满足“as-if”和“sequential-consistency with no data-race”规则。

    constexpr my_function(const int c);
    

    如果您的函数有副作用,它将阻止编译器将其移出for-loop,因为它不会满足“as-if”规则,除非编译器可以推理出它的出路。

    编译器可能会内联 my_function,对其进行归约,就好像它是循环的一部分一样,并通过不断归约发现它实际上只是一个常数,事实上删除了调用并将其替换为常数。

    int my_function(const int c) {
        return 17+c; // inline and constant reduced to the value.
    }
    

    所以你的问题的答案是......也许!

    【讨论】:

      猜你喜欢
      • 2011-12-17
      • 2010-11-17
      • 2010-10-02
      • 1970-01-01
      • 1970-01-01
      • 2019-01-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多