是否存在算术运算受到编译器优化影响的情况
Is there any case that an arithmetic operation is affected by compiler optimization?
这是一个常见的问题,但由于我主要处理gcc/g++/VStudio,所以我将其标记为c/c++。这个问题是我在考虑优化选项时想到的。在最简单的形式中,考虑一个算术运算,例如i / 6 * 8
。如果一个人面对这个表情,他很可能会把它简化为类似i / 3 * 4
的东西。如果他更喜欢乘4,他会首先这样做,即(i * 4) / 3
。我必须再次强调,这只是一个简单的例子。
那么编译器呢?他们是否有可能对这些行动采取同样的做法?既然我们知道在上面的例子中,如果i
是一个整数,那么简化和改变运算顺序可能会导致完全不同的结果,那么问题可以改为:编译器是否完全避免了这种操作?
如果我们希望程序完全按照我们所说的进行一些算术运算,并且不改变运算顺序,我们应该担心编译器的行为吗?
编译器很可能会对常量表达式应用"常量折叠"answers"常量传播"优化。
在上面的情况下,编译器不能应用这样的优化。
想象一下
i = i * (4/2)
编译器将生成
i= i * 2
这是因为不断的折叠。
编译器在优化代码方面非常保守。它们可能会改变运算的执行顺序,甚至会预先计算在编译时已知操作数的算术运算(这称为常数折叠),但它们永远不会改变计算结果。浮点运算有点麻烦。在不更改计算结果的情况下,通常无法更改计算顺序或预计算。因此,大多数编译器默认情况下保持原样。然而,可以要求编译器积极地进行优化;在这种情况下,计算结果可能会更改,但用户要求更改。例如,gcc选项-Ofast
的情况(因为它在内部设置了选项-ffast-math
)。请注意,它可能会导致奇怪的副作用,如意外的"随机"除以零。
**编辑:关于非算术运算的注释**
当代码包含指针和函数调用时,优化会变得更加困难。一般来说,预测副作用是不可能的(比如指针别名和全局变量)。因此,编译器总是以非常保守的方式放弃:一个好的编译程序至少应该是正确的,速度快是一种奢侈。
**编辑:一些例子**
这个SO问题给出了一个非常详细的例子,说明浮点会发生什么:启用优化的浮点结果不同——编译器错误?
方程简化和编译器优化之间几乎没有共同点。前者旨在使表达式更易于人类阅读,后者旨在使程序尽可能高效。像你所做的那样简化方程不会产生更快的程序,所以编译器不会为此而烦恼。
编译器无法将表达式重新排序为i * 8 / 6
,因为这可能会更改代码的含义。基本上,编译器比人类数学家聪明得多,因为编译器完全了解类型,而人类可能缺乏这种意识。编程时,i / 6 * 8
是而不是等价于i * 8 / 6
!因为存在潜在的整数溢出问题。如果编译器不知道i
的值,那么如果i * 8
不能容纳在整数中,则重新排序可能会导致溢出。
出于同样的原因,编译器也不能将代码更改为i / 3 * 4
。如果程序员想要溢出怎么办?该程序可以尝试演示未定义的行为,也可以针对溢出情况实现编译器行为。如果编译器更改值,则可能不再存在溢出,并且程序行为将发生更改,这是不允许的。
更有可能的是,编译器会在编译时通过预先计算来寻找删除其中一个操作的方法。它可能也会寻找一种方法来用比特移位取代除法,因为除法传统上是一种缓慢的操作。实际要做的优化取决于周围的整个代码。
正如其他答案所解释的,有效的编译器必须是保守的,并且不能使用任何会改变定义良好的程序行为的优化。但重要的是要记住,这种保守主义只适用于有效的、正确编写的、定义良好的程序。如果正在编译的代码依赖于未定义的行为,那么现代编译器在使用优化时可能会非常激进,而在现实世界中,这意味着所述问题的答案实际上是,"是,在某些情况下,算术运算可能会受到编译器优化的影响。"
以下是两个很棒的网页,描述了编译器在遇到未定义行为时有时会应用的一些意义改变优化:
-
"每个C程序员应该知道的关于未定义行为的知识"
-
"C和C++中未定义行为指南"
编程语言定义通常被描述为程序员和程序作为一方,编译器及其实现者作为另一方之间的"合同"。只要你的代码遵循所有规则,编译器就有义务生成一个行为与语言定义和"抽象机器"完全匹配的可执行文件。但是,如果你违反了任何规则,特别是如果你的代码陷入了任何未定义的行为,那么所有的赌注都会落空,合同无效,编译器基本上可以随心所欲。
例如,如果您编写
int i = 1;
printf("%dn", i++ + i++); /* WRONG */
您很可能会发现,表达式的值会随着优化级别的更改而更改。
(不用说,这个故事的寓意是而不是,"如果你写了未定义的代码,你必须小心使用哪些优化设置。"正确的教训是,"不要写依赖于未定义行为的代码。")
- 删除一个线程上有数百万个字符串的大型哈希映射会影响另一个线程的性能
- 空基优化子对象的地址
- 关闭||运算符优化
- 如何解决gcc编译器优化导致的centos双编译器设置中的分段错误
- C 中编译器优化的影响
- 与任何算术操作员都会影响优化的恒定操作数顺序
- gcc优化对具有明显恒定变量的循环的影响
- 对长度使用"size_t"会影响编译器优化
- 编译器优化如何影响数据加载速度
- C++优化级别是否会影响Swig Python模块的性能
- 实现move构造函数如何影响返回值优化
- 编译器优化如何影响代码逻辑
- 是否存在算术运算受到编译器优化影响的情况
- GCC 4.6.3 - 模板专用化受优化级别的影响
- goto 对C++编译器优化的影响
- 是否使用 Guard 类免受编译器优化和 CPU 重新排序的影响
- 为什么C++去构造器会影响收益值优化的行为
- g++优化选项会影响sin函数的值
- LTO优化负面影响并找到最佳解决方案
- Visual Studio警告C4743:整个程序优化如何影响_fltused