【问题标题】:Will GCC leave arithmetic with fixed values for run-time or compile the output?GCC 会将算术保留为运行时的固定值还是编译输出?
【发布时间】:2013-04-02 10:59:46
【问题描述】:

我想知道 GCC 是否会将具有固定值的算术保留在运行时执行,或者是否会将其设置为它的答案,例如。

const float halfPi = M_PI/2;

它会“归结”方程式并设置

const float halfPi = 1.57079;

还是把算法留给运行时?

【问题讨论】:

  • 看看汇编输出:gcc -S
  • GCC -O1 及以上肯定会在编译时执行此操作。事实上,GCC 中有更多代码用于处理用户定义的函数——另见 constexpr。
  • 仅供参考,此优化称为constant folding
  • 感谢 dascandy 和 unwind 的 cmets。

标签: c++ c math optimization gcc


【解决方案1】:

嗯...如果我们谈论的是积分,答案将是明确的(在通用术语下恒定折叠 )。即使是冗长的计算也可以在编译时完成......这实际上是模板非类型参数评估和(现在)constexpr 变量所必需的。

浮点表示的情况下,一旦计算变得更复杂,事情就会变得有点复杂。问题是不同大小(以及精度)的浮点表示对于相同的基本输入会产生不同的结果。

为了理解原因,假设 float 最多有 5 位精度:

   5.0000 + 0.00001
-> 5.00001
-> 5.0000 (truncation to 5 digits)

   5.0000 + 0.00001 + ... + 0.00001 (10 times)
-> 5.0000 + 0.00001 + ... + 0.00001 (9 times)
-> 5.0000 + 0.00001 + ... + 0.00001 (8 times)
-> ...
-> 5.0000

令人惊讶...对吗?如果编译是在运行时完成的,那么结果可能会根据是否使用寄存器(位宽较大)而有所不同。

因此,是否发生常量折叠可能取决于您使用的优化标志集。例如for gcc,这可能由-freciprocal-math 控制(真的不知道)。因为即使编译器确实可以做到这一点,你可能会告诉它不要(在不知不觉中)。

因此,唯一可靠的测试方法是检查编译器输出;通过检查目标代码或通过要求编译器发出程序集。并且您需要检查您使用的每个选项组合的此输出。

【讨论】:

  • +1。还有其他细节,例如std::fesetround 在运行时设置舍入行为。当foo() 可能改变舍入模式时,即使const float x=1, y=2; foo(); const float z= x/y; 也可能成为问题。
【解决方案2】:

今天是你的幸运日,因为你发现了 Agner Fog 和他的惊人深度c++ manuals!如果您查看this one8.2 部分,您会发现基本上所有编译器都能够进行常量折叠。

尽管使用您的编译器选项确保它在这种特殊情况下确实发生,但您应该按照上面的建议检查程序集输出(使用-S)。我喜欢在代码附近放置asm("MY_LABEL:"); 之类的东西以便于查找,但请记住,这会改变代码的含义,从而改变编译器解释它的方式。在这种情况下,我认为它不会改变任何事情。

【讨论】:

  • 感谢您的回答。 :) 标记为这样。
  • 其他答案有更多信息,所以我决定将其标记为将来的读者。
  • 我已经阅读汇编输出多年,但从不知道asm("label"),它作为一个时间安全器非常有用。真的值得 +1。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-05-02
  • 2013-08-29
  • 1970-01-01
  • 1970-01-01
  • 2012-06-26
  • 1970-01-01
  • 2012-09-24
相关资源
最近更新 更多