【问题标题】:Is array value as loop stop condition read every time?每次都读取数组值作为循环停止条件吗?
【发布时间】:2015-08-01 22:27:08
【问题描述】:

例子:

for (int i = 0; i < a[index]; i++) {
      // do stuff
}

a[index] 每次都会被读取吗?如果不是,如果有人想在循环中更改a[index] 的值怎么办?我自己没见过,但是编译器会做出这样的假设吗?

如果条件改为i &lt; val-2,是否每次都会对其进行评估?

【问题讨论】:

  • 应该很容易编写一个测试程序来确认这一点。
  • @OliverCharlesworth 你是对的。但也许有人对为什么事情是一种特定的方式有一些有趣的意见?
  • @user2864740 但是每次迭代都会加载一个[index] 吗? val-2 是否也每次都被评估?
  • @UserNotDefined “加载”必须在每次评估时发生,除非编译器可以绝对保证它不能在其他地方修改(或导致另一个副作用):在一般情况下这是不可能的。即使可以进行这样的优化,它也完全取决于使用的编译器和标志;查看特定编译器的输出(GCC 的 -S 标志)可能在这里有用。
  • @OliverCharlesworth:这实际上只是确认编译器生成的代码确实(不)优化了访问。但这不是一个有效的一般证明。

标签: c loops for-loop memory


【解决方案1】:

当系统不受程序其他部分的影响时,编译器会正常进行优化。因此,如果您在for 循环内对条件参数进行更改,编译器将不会优化。

如前所述,编译器应在代码 sn-p 的每次迭代之前读取数组并检查它。您可以如下优化您的代码,然后它将只读取一次数组以进行循环条件检查。

int cond = a[index];
for (int i = 0; i < cond; i++) {
      // do stuff
}

【讨论】:

  • 编译器不是也必须保证不会出现别名吗?
  • @UserNotDefined 它取决于我们在编译代码时选择的编译器优化级别。 dbp-consulting.com/tutorials/StrictAliasing.html
  • @UserNotDefined:是的。
  • @Steephen:不是,不是。可观察行为由标准定义,不受实现优化级别的影响(可以说是复制 ctor 省略)。而且您的解决方案没有任何改变——现在编译器只需要确定cond 是否改变,而不是a[index] 是否改变。毫无意义。
  • @LightnessRacesinOrbit 我同意你的看法。我从广泛的角度阅读了 OP 在评论中的问题,例如编译器优化如何影响别名
【解决方案2】:

嗯,也许吧。

符合标准的编译器将生成的代码表现得好像 每次都读取。

如果index 和/或array 属于存储类volatile,则每次都会重新评估它们。

如果它们不是并且循环内容没有以可以预期修改其值的方式使用它们,则优化器可能会决定使用缓存结果。

【讨论】:

  • volatile 类是告诉编译器每次重新计算表达式的唯一方法。其他编译器行为不可修改。
【解决方案3】:

Co 不会将表达式的结果存储在临时变量中。因此,所有表达式都在原地重新评估。请注意,任何for 循环都可以更改为while 循环:

for ( def_or_expr1 ; expr2 ; expr3 ) {
    ...
}

变成:

def_or_expr1;
while ( expr2 ) {
    ...
cont:
    expr3;
}

更新: for 循环中的 continue 与上述 while 循环中的 goto cont; 相同。 IE。 expr3 会针对每次迭代进行评估。

编译器基本上可以应用任何可以证明不会改变程序本质的优化。描述完整的细节对于这一点来说太过分了,但总的来说,它可以(并且将会)优化:

  • a[index] 在循环中未更改:在循环之前读取一次并保存在临时(例如寄存器)中。
  • a[index] 在循环中被更改:用新值更新 temp(寄存器),避免内存访问(和索引计算)。

为此,编译器必须假定数组在可见控制流之外没有改变。这通常是正在编译的文件(包含所有包含的文件)。对于使用链接时间优化 (LTO) 的现代系统,这可以是整个最终程序 - 减去动态库。

请注意,这是一个非常简短的描述。实际上,C standard 非常清楚地定义了程序必须如何执行,因此编译器可以优化什么/如何优化。

如果数组被更改,例如被中断处理程序或另一个线程更改,事情就会变得复杂。根据您的目标,您需要从volatile、原子操作(stdatomic.h,自 C11 起)到线程锁/互斥锁/信号孔/等。控制对共享资源的访问。

【讨论】:

  • for 当循环包含continue 时与while 不同
  • @Jasen:谢谢,我忘了。已更新。
猜你喜欢
  • 2011-12-03
  • 2021-08-26
  • 2014-05-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多